Matthew Rossi
Lisbon, PortugalVol. I · No. VII · October 2026
Software engineer turning toward sustainable urban mobility
← Blog

What Building the Atlas Taught Me About AI

This is the last of three posts about building the Urbanist Atlas with Claude Code. The first two covered the code — the Go API and the React frontend — and then the data, where modeling cities and verifying organizations turned out to be harder than the code.

This one steps away from the technical and gets to what I actually learned.

Where AI fell short

More than once I stopped and asked whether this was actually saving me time. The loops are long, a lot of the day goes to waiting for an agent to finish, and reviewing the same output for the fourth time is genuinely mind-numbing.

Some of that was the models and some of it was the workflow they create. There’s an art and a craft to writing software, and that becomes harder to access when your day goes to reading generated code instead of writing your own. The code that comes back is functional and rarely better than that: something trained on a vast corpus of existing code tends toward the familiar, which isn’t the same as the right answer for your particular problem.

Despite all the human review I built into the process, not writing the code myself made it hard to fully and accurately know what I was working with. When I write code, the act of writing it forces me to construct a mental model in real time. When AI writes it, I have to construct that mental model through review. It feels analogous to handwriting vs. typing — handwriting is going to do a better job of committing something to memory.

And the end result isn’t the only thing that has to be right. I still had to flesh out the security parameters, and make the deploy strategy robust enough to survive an intermittent failure. I still had to decide how third-party libraries got imported and used, and how the abstractions across the network, database, and hardware layers fit together. None of that shows up in a demo that runs.

AI can do all of that. It’s often good enough, and what it produces usually runs. But is it really the best answer to the specific engineering problem in front of you? Working this way makes that question harder to answer, not easier.

The energy and environmental cost

I paid for one month of a Claude Max subscription to do this. That’s $100. To say nothing of the fact that many people can’t afford that sort of budget, there are a lot of tokens spent on each subagent, each feature, each AI-assisted review cycle or organizational pass. Data centers are expanding rapidly to meet increased demand. In my home state of Michigan, at least thirteen data centers are in various planning stages, and residents are organizing against them. They cite impacts on their towns’ water supply, power grid, and rural character.

I did make some deliberate choices not to reach for the biggest model by default. Opus was my primary one, but simpler subagent work — a documentation pass, a formatting cleanup, a first read of a diff — went to Haiku or Sonnet instead. Those are smaller models: fewer calculations per word, and correspondingly less electricity per answer. I tried to keep agents few and tightly scoped rather than spawning a swarm just because I could, and to batch review passes instead of re-running them every time I changed my mind.

I had this in the back of my mind when writing the Atlas, and I admit it’s a hard trade-off that I didn’t have a good answer to. The concept of tokens often feels really abstract, so I can’t piece together how compute-intensive these tasks are. It was restraint by instinct rather than by instrumentation, which is a weaker answer than the question deserves.

Five lessons from building with AI

That’s the code, the data, and where it fell short. Here are the five takeaways I keep coming back to for using AI effectively. They all boil down to the same thing: you have to know enough about the problem to challenge the answer.

Be very explicit with your input

There is a strong difference between “I want to build a directory of safe street organizations” and what I actually used as my starting point, which was something more like this:

“We are going to build a directory of safe-streets and urban advocacy organizations. I want this to be a modular application that can be consumed by a number of consumers. To start, let’s build this in three pieces, starting with a simple API. This API will be built in Go. You should default to using standard libraries wherever possible, and ask before importing third-party dependencies. Build it with a focus on TDD. Plan a skeleton of what this will look like.”

Every clause in there has a purpose. TDD is test-driven development — writing tests alongside, or in the strictest version before, the implementation — and asking for it up front meant tests existed from the first commit instead of being bolted on months later, when nobody, me included, wants to write them. Naming the standard library as the default is what kept the dependency list short. None of that happens if the prompt is one sentence long, and the model was never going to volunteer any of those constraints on its own.

That opening prompt for the Atlas calls for Go, and the first post covered how that choice got made: I left the stack open-ended, Claude suggested Node, and I turned it down because I had specific reasons — static typing, a straightforward concurrency model, and a language I’d shipped a production application in recently. So being explicit improves the model’s output, but the bigger gain is doing that thinking yourself — it gives you something concrete to hold the model’s answer against. If you can’t say why you want something, you’ll take whatever you’re handed.

Never take the first result

I’ll admit that “never” is a strong word here. However, it was pretty rare that I was fully satisfied with the first result that the AI would return to me. It might “work,” but perhaps the code wasn’t structured well. There were some edge cases or tests missing. Or I realized there would be a better abstraction I could pull out somewhere.

I did multiple passes of simply “look for refactor opportunities” or “look for simplifications” — or, if I spotted a specific issue, I’d just tell the AI what I wanted. This is where our job as software engineers shifts a little bit from writing the code to architecting it. That’s the ground the old acronyms cover — SOLID (five design principles about keeping responsibilities separate and swappable), YAGNI (“you aren’t gonna need it”) — and they mostly come down to building the thing you actually need, in a way that the next person can change without fear. Claude doesn’t always land there on its own, so you have to say so. (See point one: be explicit with your input.)

The region graph from the second post is the clearest case of this. I seeded that brainstorming session with New York, Chicago, and San Francisco specifically because I knew those three places well enough to push back on a bad answer, and the multi-parent graph is what came out of the pushing. Tiers are the obvious way to model where somewhere is: a city inside a county inside a metro inside a state. They’re also often wrong, in a way you only catch if you already know that Park Slope has no border to look up and that Chicagoland crosses a state line.

That’s iteration: working the same problem with the same tool until the answer is good. It’s necessary. But everything inside that loop is still being judged by the thing that produced it.

Review, review, and then review again

Fresh context does the real work here. An agent that just spent an hour building something is carrying every assumption it made along the way, and it’ll usually grade its work against those assumptions instead of against reality. So do people — it’s why we invented code review in the first place, and why nobody proofreads their own writing well. The operative word is fresh, not artificial.

Once a plan was drafted, I reviewed it and pushed back. Finally, after plenty of back and forth, I let it build. After it was built, I threw another agent with a fresh context to review the code that was generated. After two or three rounds of this, I’d do my own code review, leaving feedback, doing manual security analysis, and then throw another agent on it to address the feedback.

The polygon function from the second post is why I kept doing this even when it felt like overkill. That code ran clean, returned a number, and the number was entirely plausible — a few postal codes just quietly resolved to the wrong metro. No amount of iterating would have surfaced that outcome, because there was nothing there to look wrong. Only something that had no hand in producing the answer catches a bug like that: a fresh agent, a deterministic regeneration that has to reproduce what’s committed byte for byte, or a person reading it slowly.

There are open-source extensions for Claude Code that formalize this kind of loop, so you don’t have to hold the whole process in your head. I used Superpowers, which packages workflows like brainstorming, planning, and code review into skills the agent reaches for on its own. Pick a different tool or none at all; the shape is the part worth copying — small, focused scopes, and a review done by something that didn’t write the code in the first place.

Know where the model is weakest

These models are trained on an enormous body of existing human work — it’s the whole reason they’re any good. That tells you where to aim your reviews.

A system like that is strongest where your problem is ordinary, because ordinary is what it learned from. It’s weakest where your problem is strange. Every hard thing in this project sat at the strange end: a ZIP code whose neighborhood has no border you can look up anywhere, two federal datasets that disagree about what year it is, a polygon with a hole in it, whose area comes out too big unless you subtract the hole. Claude handled the ordinary parts of the Atlas faster and more thoroughly than I would have alone. The parts that were specific to this project are where I had to slow down.

So the practical version is: hand off the ordinary parts freely, and make a list of the parts that are unusual. That list is where AI will be confidently, plausibly wrong, and it’s where your review time should go.

Know what only you can decide

The six inclusion criteria from the second post were the easy half — local focus, active in the last year, on topic, no government agencies, no consultancies, no housing or YIMBY groups. No training corpus was going to hand me that last one. Housing and YIMBY work is a neighboring movement and a friendly one, and I left it out anyway, Open New York included, because a directory that indexes everything adjacent stops being useful for the thing you came to it for. That’s a decision about what the Atlas is for, and I’d make it again.

The harder half is the cases the six rules don’t reach. PedSafe Vegas clears all six — it’s local, it’s active, it’s squarely on topic, and a university research center isn’t the sort of government agency that rule was written for. I excluded it anyway, because it’s a program run out of that center rather than an organization in its own right. I’d already made that call once, about a local chapter that doesn’t incorporate separately from its state organization. Neither decision appears anywhere in the published criteria. The rules that actually govern the Atlas are the ones I’ve never written down — and couldn’t have, because I didn’t know them until a case like PedSafe forced them.

Once criteria like these exist, a model can apply them very well. Someone has to have chosen them in the first place, keep choosing in the cases nobody wrote a rule for, and be willing to defend those choices to the people they affect. That list of cases is still growing, and each new one is decided before there’s a rule to apply to it.

I keep coming back to Data, the character from Star Trek: The Next Generation. Data is an extremely capable artificial life-form — he can execute nearly anything asked of him — but the character is compelling because of what he can’t originate: wanting things, valuing things, choosing what matters. That’s most of this project, in the end. The Atlas is a search box sitting on top of a few hundred judgment calls, and the judgment is the part that had to be mine.

If you want to try something like this

Getting from an idea to a working prototype has never been easier than it is with these tools, and I do hope that means more good ideas come to life that otherwise wouldn’t. But I’d be wary of any post handing you a process to copy, this one included. The specific shape of this project came out of the experience I brought to it — thirteen years of backend engineering, almost no frontend, and a subject I care about enough to argue with a model about. Your version will come out of something different. In the parts of a project where you know the domain, hand off aggressively and review fast. Where you don’t, slow down — or the model’s plausible answer will get expensive to untangle.

Beyond the model choices I mentioned earlier, the tooling is all there to reuse. Since the Atlas is open source, I’d encourage you to look at my just file if you want to take any of it — like the containerized link validation that checks every organization’s website through a VPN and flags the dead ones.

Why I’m being transparent about AI

The kind of reuse I described is only possible because everything is openly licensed. The code, data, and written content are all publicly available under the Apache 2.0, ODbL-1.0, and CC-BY-SA-4.0 licenses, respectively. I’m not seeking monetary compensation for the project, aside from a small tip jar. I strongly encourage folks not to throw coins into it unless they really want to, and have already thrown some coins into the jar of their local advocacy orgs first.

I hope you will reach out, submit corrections, and tell me about a new advocacy org you’d like included. Trust has to be a core value, and trust is built from being open and transparent. Hiding behind a robot, or not being forthcoming about the fact that I am using AI to build, operate, and maintain the Atlas, would erode the trust that I want people to place in me.

AI is going to reshape a lot of how we work in the coming years, and there are powerful incentives pushing that momentum in the wrong direction — maximizing profit even when the costs are borne by people and the planet. Looking back, nearly every place this project succeeded or failed came down to what I handed off to the model and what I kept.

I’ve met some incredible activists over the years, and have been inspired by their work and passion for their homes. This project is a love letter to all of them, and my small way of doing my part for safer, more livable cities.