Q3 2023

Growth

Hiring, DevMode, and Multi-Region Progress

Real usage produces real problems, and Q3 was the quarter I started feeling both.

The hiring process crystallized into something I could actually execute. Instead of the dreaded whiteboard hazing of big tech, I designed a two-phase introduction: a multiple-choice test proving you read the manual, then a small paid project ($250 to $1000) building something with Adama. I'd been running this through Upwork with success -- the chess game from Q2 came out of this exact pipeline.

I was looking for three categories of people: grinders who execute atomic tasks (convert this image to HTML, write this E2E test), builders who pull from the roadmap and deliver contracted features, and operators who help with production and high-touch customers. The core problem in life and business is trust, and I believed the path to operator starts with grinding and building. Facebook had bootcamp for this reason -- every employee learned the tools first. Code is a battle, and few engineers trust leaders who haven't been in the trenches.

On the technical side, the development experience was embarrassing. The system was designed around me to amplify me, which works great in solo mode but is operationally irresponsible when multiple developers share a single production environment. Frontend developers needed fast iteration -- ctrl-S to browser in seconds, not minutes. Backend developers needed a solo mode to test locally. Fullstack developers needed to iterate on Adama's JavaScript client without deploying to production.

I shipped the devbox: a local development server with file watching and hot reload. java -jar ~/adama.jar frontend dev-server from the command line, point it at your working directory, and changes manifest in the browser on save. For fullstack work, you could point it at a local copy of the client JavaScript. The devbox would grow throughout the year into something truly great -- by December it had a reactive debugger with dark mode, attachment support, and language server protocol integration for IDE errors.

The domain-specific language philosophy got a proper defense in July. My argument: we build products in too open of domains. There are too many choices -- React vs Vue vs whatever, node vs PHP vs Ruby, Postgres vs MongoDB vs DynamoDB. The current situation is only good for developers who want a steady job, not for small businesses who need things that work. RxHTML is not a typical framework. It asks what HTML didn't have, and adds it. Sixteen commands, six new elements, a few pieces of glue. If I'm gloriously wrong about this whole thing, any reasonable developer can convert the RxHTML templates to React. The definition is that small.

Multi-region progress arrived in August. The V1 plan was straightforward in concept, brutal in execution. Control plane operations (authentication, deployments, domain management) flow to a global region. Document connections are regionally homed -- if you connect to an archived document, it restores within your local region, giving you 5ms read/write latency. If the document lives in another region, your latency is local plus inter-region. Documents bias toward the first person who loads them, which is great for local businesses.

The hard part was the migration. My early mistake was "just talk to the database" which helped me move fast defining the product but hurt as I thought about performance. I was turning synchronous database calls like Domain domain = Domains.get(nexus.database, host) into async interfaces like domainFinder.find(host, new Callback<Domain>() { ... }). It was especially obnoxious given that Java's virtual threads were becoming a thing, but I'd grown accustomed to managing executors and callbacks without much fuss.

September brought a real customer problem that taught me about the limits of my data model. Nested tables -- tables within tables -- create a document-store-like structure that makes certain queries complicated. The scenario: users belong to groups, groups sign up for activities, and the nested structure means answering "which activities am I signed up for" requires scanning every activity. The immediate fix was an association table with indices, plus a @load event to migrate existing data. But the deeper issue was that Adama has no joins, and this is intentional. Streaming environments make query planning hard, and joins naturally require thinking about indices and optimization.

I sketched out a "waterfall" syntax for virtualizing nested tables into flat queryable structures, and a plan for smarter reactive invalidation where tables publish specific events rather than "hey, something changed." If 1,000 people are connected and you insert a user, all 1,000 recompute their view even if nothing changed for them. Instead of blanket invalidation, tables could publish typed events -- index_change(field, value), primary_key(value) -- and formulas could filter the noise.

The wealth post in September captured where my head was philosophically. Buy good things that last. An Excel spreadsheet from 1998 still works today -- that's 25 years of something working. Where is the Excel for internet applications? That's Adama's ambition: infrastructure that just works. Every aspect considered from the lens of whether the idea is correct for hundreds of years. Division by zero returns a maybe<double>. The sqrt function returns a complex number. List lookups return maybe<T>. Forced discipline against the billion-dollar mistake of null references.

The quarter ended with community members building tutorials, a devbox that worked, multi-region taking shape, and real customer problems forcing the platform to grow up.