▲ 19 r/golang

wrote a cli property deal scorer in go and goroutines made the batch lookups absurdly fast

a friend invests in rental properties and he kept sending me lists of addresses asking me to look them up on zillow. like 15-20 addresses at a time, usually from a mailer list he gets from his county. i'd spend 20 minutes tabbing through zillow pages and texting him back which ones were worth looking at.

wrote a cli tool in go that takes a text file of addresses, looks them all up in parallel, scores each one, and spits out a sorted list. the whole thing is a single binary he runs on his laptop.

the property data comes from a rest api called zillapi. you give it an address, it returns zillow data as json. zestimate, asking price, rent estimate, tax records, price history. 300+ fields per property. i only use about 10 for the scoring but the full response is there if i need more later.

the interesting part is the concurrency. the api takes about 1-2 seconds per call. doing 20 addresses sequentially would take 30+ seconds. with goroutines and a semaphore it takes about 3 seconds total:

go

func scoreAll(addresses []string) []Result {
    sem := make(chan struct{}, 10)
    results := make(chan Result, len(addresses))

    var wg sync.WaitGroup
    for _, addr := range addresses {
        wg.Add(1)
        go func(a string) {
            defer wg.Done()
            sem <- struct{}{}
            defer func() { <-sem }()

            data, err := lookupProperty(a)
            if err != nil {
                results <- Result{Address: a, Error: err}
                return
            }
            results <- Result{
                Address:    a,
                Zestimate:  data.Zestimate,
                AskPrice:   data.Price,
                RentEst:    data.RentZestimate,
                Score:      scoreDeal(data),
            }
        }(addr)
    }

    go func() {
        wg.Wait()
        close(results)
    }()

    var out []Result
    for r := range results {
        out = append(out, r)
    }
    sort.Slice(out, func(i, j int) bool {
        return out[i].Score > out[j].Score
    })
    return out
}

semaphore of 10 so i'm not hammering the api with all 20 at once. the scoring is simple. rent-to-price ratio above 0.8% and asking price below zestimate is green. one condition met is yellow. neither is red. the output is sorted best to worst so the green deals are at the top.

the cli usage looks like:

$ propertyscore -file addresses.txt

GREEN  1425 Oak St, Springfield    ask: $285k  zest: $312k  rent: $2,100
GREEN  892 Maple Ave, Springfield  ask: $195k  zest: $218k  rent: $1,650
YELLOW 3301 Pine Rd, Springfield   ask: $340k  zest: $335k  rent: $2,800
RED    710 Elm Ct, Springfield     ask: $420k  zest: $389k  rent: $2,200

my friend runs this every tuesday when his county mailer list comes in. takes 3 seconds for 20 addresses and he immediately knows which ones are worth driving by. before this he was spending an hour on zillow every tuesday morning.

for the ai side i also set up a skill so he can ask claude about properties:

npx clawhub@latest install zillow-full

the binary is 8mb. no dependencies beyond the stdlib and the standard json/http packages. i compiled it for his windows laptop with GOOS=windows and just texted him the exe. no docker, no install process, no runtime. that's the part of go i appreciate most for tools like this.

reddit.com
u/Less-Loss1605 — 28 days ago

sveltekit made my property analysis tool feel way faster than it actually is

built a property lookup tool for a friend who invests in rentals. he pastes an address, gets back a deal score based on zestimate vs asking price and rent-to-price ratio. can save properties and compare them side by side. nothing groundbreaking but he uses it every morning.

i built the first version in react and it was fine. worked. but the comparison page felt sluggish. selecting 4 properties to compare meant 4 api calls and the whole page waited for all of them before showing anything. i tried fixing it with suspense boundaries and it got complicated fast.

rewrote it in sveltekit over a weekend and the comparison page feels instant now. not because sveltekit is faster at fetching data. the api calls take the same 1-2 seconds. but sveltekit's streaming with load functions means each property card renders the moment its data arrives. no waiting for the slowest one.

the load function for the comparison page:

ts

export const load = async ({ url, fetch }) => {
  const addresses = url.searchParams.getAll('addr');
  return {
    properties: addresses.map(addr =>
      fetch(`/api/property/${addr}`).then(r => r.json())
    ),
  };
};

returning an array of promises. sveltekit streams each one to the client as it resolves. in the template i just use await blocks:

svelte

{#each data.properties as propertyPromise}
  {#await propertyPromise}
    <PropertyCardSkeleton />
  {:then property}
    <PropertyCard {property} />
  {:catch}
    <PropertyCardError />
  {/await}
{/each}

each card shows a skeleton, then fills in when its data arrives. if one address is cached server-side it renders immediately while the others are still loading. my friend compared 4 properties yesterday and the first two showed up in under a second. the other two filled in about a second later. in the react version he stared at a spinner for 3 seconds.

the property data comes from a rest api called zillapi that returns zillow data as json. the backend is a sveltekit api route that proxies the call and adds a deal score. green if asking price is below zestimate and rent-to-price is above 0.8%. yellow for one condition. red for neither. the api returns 300+ fields per property and i store the full json in a postgres jsonb column so i can add new fields to the dashboard without re-fetching.

the save/unsave feature is a form action with use:enhance. no client-side state management at all. the form posts, the server updates the database, the page revalidates. the button optimistically switches to "saved" because i set the form action to use:enhance with a callback that updates the ui immediately. if the server errors it reverts. the whole save flow is about 20 lines total between the action and the component.

for the ai side i set up a skill so he can ask claude about his properties:

npx clawhub@latest install zillow-full

the total codebase is about 900 lines including styles. the react version was 2400 lines and felt worse. most of the savings came from not needing tanstack query, not needing a state management library, and form actions replacing the mutation/loading/error boilerplate. sveltekit just has less ceremony for this kind of app.

reddit.com
u/Less-Loss1605 — 1 month ago
▲ 16 r/reactjs

built a property analysis dashboard for a friend and tanstack query made the data layer almost trivial

a friend who invests in rental properties kept texting me addresses asking "is this a good deal." he'd send me a screenshot from zillow and want me to run the numbers. after doing this like 30 times i figured i'd just build him a dashboard.

the backend is a simple express api that proxies a rest api called zillapi. you pass it an address, it returns zillow data as json. zestimate, asking price, rent estimate, price history, tax records, school ratings. 300+ fields per property. the backend just forwards the response and adds a deal score i calculate server side.

the react side is where i want to focus because tanstack query turned what i thought would be a complicated data layer into almost nothing.

the property lookup is a single useQuery call:

tsx

function useProperty(address: string) {
  return useQuery({
    queryKey: ['property', address],
    queryFn: () => fetch(`/api/property/${address}`).then(r => r.json()),
    staleTime: 1000 * 60 * 60,
    enabled: !!address,
  });
}

staleTime of one hour because zestimates don't change more than once a day. if my friend looks up the same address twice in one session the second load is instant from cache. he didn't ask for this but he noticed immediately. "why was that one so fast?"

the comparison feature was where tanstack query really paid off. my friend wanted to paste 3-4 addresses and see them side by side. i was dreading managing loading states for multiple parallel requests. turns out useQueries handles it:

tsx

function useCompare(addresses: string[]) {
  return useQueries({
    queries: addresses.map(addr => ({
      queryKey: ['property', addr],
      queryFn: () => fetch(`/api/property/${addr}`).then(r => r.json()),
      staleTime: 1000 * 60 * 60,
    })),
  });
}

each address resolves independently. if one was already cached from a previous lookup it shows up instantly while the others are still loading. the comparison table renders progressively as each result comes in. i didn't build any special loading logic for this. tanstack query just did it.

the saved properties feature uses useMutation with optimistic updates. when my friend saves a property it appears in his list immediately and the POST happens in the background. if it fails it rolls back. the mutation also invalidates the saved list query so the count stays correct. all of that is maybe 15 lines:

tsx

const saveMutation = useMutation({
  mutationFn: (property) => fetch('/api/saved', {
    method: 'POST',
    body: JSON.stringify(property),
  }),
  onMutate: async (newProperty) => {
    await queryClient.cancelQueries({ queryKey: ['saved'] });
    const previous = queryClient.getQueryData(['saved']);
    queryClient.setQueryData(['saved'], old => [...old, newProperty]);
    return { previous };
  },
  onError: (err, vars, context) => {
    queryClient.setQueryData(['saved'], context.previous);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['saved'] });
  },
});

before tanstack query i would have had a loading state, an error state, and a data state managed with useReducer for each of these features. probably 100 lines of state management code. instead it's 3 hooks that handle caching, deduplication, loading states, error handling, and background refetching. the entire data layer for this app is about 40 lines.

for the ai side i set up a skill so he can ask claude about his properties:

npx clawhub@latest install zillow-full

my friend has been using it daily for 2 months. 3 other investors from his meetup asked for accounts so i added auth with clerk and a user_id on saved properties. the app feels fast because of the caching and the progressive loading on comparisons. none of that speed required me to think about performance. tanstack query just does the right thing by default.

reddit.com
u/Less-Loss1605 — 1 month ago
▲ 55 r/SoftwareTips+1 crossposts

what software have you been using for years that never gets mentioned anywhere

for me it's sharex. i've been using it for like 4 years for screenshots and screen recordings and i never see anyone talk about it. it does annotations, auto upload to imgur, gif recording, ocr on screenshots, scrolling capture. every time someone recommends snipping tool or lightshot i wonder if they know sharex exists.

the other one is everything by voidtools. windows file search is useless for actually finding files fast. everything indexes your entire drive and finds any file in milliseconds. i open it probably 10 times a day and i've been using it since like 2019. it's one of those tools where if it stopped working i'd genuinely not know how to use my computer anymore.

what's your version of this? something you use constantly that nobody ever recommends.

reddit.com
u/Less-Loss1605 — 1 month ago
▲ 136 r/homelab

what's running on your homelab right now that you actually use daily

i feel like half the homelab posts are people showing off 42U racks with enterprise switches and then admitting they mostly use it for plex. no shade, i've been there. but i'm curious what services people actually open every day vs what's running because you set it up once and forgot about it.

my daily use list is pretty short. immich for photos, paperless-ngx for scanning documents, vaultwarden for passwords, and a reverse proxy with caddy tying it all together. that's it. everything else i've installed i either stopped using after a week or it just sits there doing its job without me thinking about it.

what's on your actually-use-it-daily list?

reddit.com
u/Less-Loss1605 — 1 month ago
▲ 970 r/OReilly_Learning+1 crossposts

what's a script you wrote once that's still saving you time years later

i wrote a powershell script like 3 years ago that checks AD for disabled accounts that still have active mailboxes and spits out a csv every monday morning. took me maybe an hour to write. it's caught orphaned mailboxes so many times since then that i stopped counting. the licensing cost it's saved us is probably more than my raise last year.

the other one is a bash script on our linux boxes that monitors disk usage and sends a slack alert when anything hits 85%. nothing fancy, just df piped through awk with a curl to the slack webhook. wrote it after we had a production outage because /var/log filled up and nobody noticed. that was a fun 2am call.

what's your version of this? the one script that keeps quietly doing its job in the background.

reddit.com
u/Less-Loss1605 — 1 month ago