Start With Postgres, Then Earn the Complexity

In the year of our lord 2026, it is still a little wild how quickly a new application turns into a shopping cart full of infrastructure.

A database, obviously. Then a search cluster, because searching is hard. A queue, because background work is hard. Redis, because speed. An analytics warehouse, because charts. A vector database, because it is 2026 and apparently every application needs to have a thoughtful conversation with its own invoices.

Before anyone has used the product, the architecture diagram looks like a cloud-vendor scavenger hunt.

Just use Postgres.

More precisely: start with Postgres, and make every additional system earn its way into the application. Not with “we may need this at scale,” but with a concrete requirement the database cannot meet.

This is not the argument that Postgres is secretly every kind of database. It is not. It is the argument that operational complexity is real, and a mature relational database can cover an almost rude amount of early and middle-stage application work.

Every box comes with chores

A new service is never just one more friendly logo on the diagram. It is another deployment, backup policy, access model, alert, upgrade path, incident mode, and bill. It is another thing to explain to the next engineer who joins the team.

The entertaining part begins when data has to exist in more than one place. The application writes a record to Postgres. A worker copies it to the search index. Another process invalidates a cache. Somebody notices the analytics event is missing. Now the team has a distributed-systems problem, which is a very sophisticated way to say “we are trying to keep our copies from disagreeing.”

That can absolutely be worthwhile. It is just not free because the managed-service setup wizard was pleasant.

Postgres lets a team postpone much of that ceremony. It gives you transactions, indexes, full-text search, structured queries, and support for semi-structured data in one well-understood place. That leaves more time to find out whether anyone wants the product.

The boring default has range

Most applications start with related records that need to be correct: users, accounts, permissions, orders, messages, audit logs, tasks. A relational database is not a compromise for that workload. It is the obvious tool.

It also has more room than people sometimes remember:

  • Search: Built-in full-text search may be plenty when users need to find things in your application, rather than search the internet with all the relevance expectations that implies.
  • Flexible attributes: JSONB is useful when a few fields genuinely vary, while the facts the application relies on stay in columns with types and constraints.
  • Background work: A table-backed job queue can be a practical answer for modest asynchronous work, especially when creating the job must be atomic with a database update.
  • Reporting: Indexes, aggregates, and materialized views can answer a surprising number of product and operational questions before a separate analytics pipeline becomes necessary.
  • Retrieval features: Vector extensions can make a small semantic-search or retrieval feature easier to prototype beside the records it is retrieving.

None of this means dedicated tools are fake. It means “could Postgres handle the first honest version?” is a much better question than “which six services should we provision before lunch?”

“We might need it later” is not a requirement

There is a familiar performance of technical seriousness where a team rejects the simple design because it might not survive the eventual day it has 100 million users, a global event stream, and a full-time platform group.

Maybe! That would be a great problem to have. It would also be useful to know whether the product needs 100 users first.

A specialized system should have a job description:

  • A dedicated search engine makes sense when relevance requires sophisticated ranking or faceting, indexing load is hurting the primary database, or search must be available independently of transactional traffic.
  • A message broker makes sense when workloads need durable fan-out, replay semantics, very high throughput, or consumers that scale independently.
  • A cache makes sense when the database cannot meet a known latency or read-volume target at an acceptable cost.
  • A warehouse makes sense when analytical queries should not compete with transactional work, or when the organization needs durable historical analysis across many systems.

Those are requirements. “We saw a conference talk” is a mood.

One system does not mean one terrible system

Using Postgres for more than the obvious tables is not permission to put every fact in one 400-column table called “data” and call it flexibility.

The fundamentals still matter, perhaps more so when one system is carrying a meaningful share of the application:

  1. Model stable facts explicitly. Use types, foreign keys, and constraints where integrity matters. Use flexible fields deliberately, not to avoid deciding what data means.
  2. Measure real queries. A slow query is a reason to inspect indexes and query plans before it is a reason to buy a different database.
  3. Make background work observable. A table-backed queue still needs retries, failure handling, and a clear answer for what happens when a worker disappears at 2:00 AM.
  4. Know the escape route. When a specialized service becomes necessary, decide what is authoritative, how data is copied, how correctness is checked, and what happens when synchronization falls behind.

The goal is not to prove that Postgres can be forced into every workload. Congratulations, nobody gets a medal for that. The goal is to avoid paying for distributed complexity until it pays you back.

AI can help with the code. It cannot un-move the data.

In 2026, Codex and Claude can do useful work on technical debt. They can help trace a legacy code path, draft a refactor, write tests, or turn an unpleasant migration plan into a less unpleasant set of pull requests. That is genuinely useful.

They cannot make a bad datastore decision disappear.

Once production data lives at meaningful scale in the wrong place, the hard part is rarely producing the migration script. It is moving data without losing it, corrupting it, leaking it, duplicating it, or letting two systems quietly disagree while the migration is in progress. Then there are backfills, validation, cutover, rollback, client compatibility, and the small matter of keeping the application available while all of this happens.

An AI assistant can help reason about those steps. It cannot turn a risky data migration into a harmless autocomplete exercise. The more systems that become authoritative for some slice of the product, the more expensive the eventual exit tends to be.

That is another reason to start simple. Choosing Postgres does not guarantee you will never migrate data; products change. It does mean you should not create a migration project merely because an early architecture diagram needed more logos.

Make complexity pay rent

Some applications need specialized infrastructure on day one. A search product with sophisticated relevance requirements is not going to bluff its way through with a basic text index. A high-volume eventing platform should not pretend a table queue is its final form. A real-time analytics product may have needs that are plainly not OLTP.

Fine. Use the right tool.

But most teams should be able to state the specific benefit they are buying: a reliability property, a measured performance target, or a product capability that the simpler design cannot deliver. If they cannot, the new box is probably architecture cosplay.

Postgres is not everything. It is just a very capable foundation that can let a team ship, learn, and defer irreversible decisions until there is evidence behind them.

That is less glamorous than a diagram with twelve logos. It is also often how you end up with an application instead of a collection of services that is very prepared to support one.

This post was sparked by Raphael Bauer’s “PostgreSQL for Everything”. His essay goes deeper on the capabilities that make the “start here” argument possible.

Codex Built My Kalshi Bot. I Still Couldn’t Find an Edge.

I, like I imagine most people on here, dream about making money while I sleep. And not the boring “market goes up” money, but something sexier: uncapped alpha because you figured something out that no one else has.

With that idea rattling around my brain, I listened to an Odd Lots episode about a group of traders consistently winning in Kalshi prediction markets.

Naturally, since “coding is solved,” I had to give building a Kalshi bot a shot.

Tl;dr: I traded around $500 and ended up about even.

That is not a tragedy. It is just a much more useful result than the version where I tell you I had an AI build a money printer over a weekend.

Picking a market

Kalshi has a lot of markets. I wanted something with good public data and a quick resolution cycle, mostly because I did not want to wait months to find out whether my very sophisticated robot had learned anything.

Weather fit pretty well. There is plenty of publicly available weather data, and the markets settle quickly enough to get feedback. So down the rabbit hole Codex and I went, building a bot to trade weather events.

The initial setup was honestly pretty impressive. Codex got the plumbing in place fast: risk controls, backtesting tools, and a slick dashboard. The kind of stuff that used to be enough work to make a small experiment feel like a real project before you even had a hypothesis worth testing.

Now it was a real project. Which meant it was time to find a real strategy.

The part nobody can code for you

This is where I got stuck, with Codex looking at me with lost-puppy eyes.

We tried a few things:

  • ML models to predict price movement
  • a market-making strategy in backtests
  • fast-following momentum trades

All of them managed to make approximately $0.

This is an important distinction that is easy to lose when the tooling is this good. Building the system and finding the edge are different problems.

The system needs data collection, order handling, position limits, monitoring, a way to replay decisions, and enough guardrails that a bad assumption does not become an expensive bad assumption. Codex was enormously helpful with that work. It makes it much easier to turn an idea into something you can actually test.

But an edge is the thing that makes the test worth running. It is a reason to believe your estimate is better than the market’s after accounting for fees, execution, timing, and everyone else who has access to the same obvious public data. “Use the weather forecast” is not really an edge when the people setting prices and trading against you can also use the weather forecast.

Backtests are very polite

The backtests were useful, but they also have a way of being polite. A strategy can look reasonable until you remember that historical data does not necessarily capture the price you could have traded at, how quickly the market moved, or whether the market had enough liquidity when you wanted it.

And then there is the more basic problem: a model that predicts something about the weather is not automatically a model that predicts a market price. The trade only works if the model finds information the price has not already absorbed, or if it identifies a repeatable way the market is mispricing that information.

I did not find that.

Trading about $500 and ending roughly flat is a small sample, not a statement about Kalshi, weather markets, or prediction markets in general. It was enough, though, to kill the idea that the hard part was connecting some APIs and writing a model.

Codex gives you leverage, not special sauce

I came away more convinced that Codex and friends give you enormous leverage if you “know what you’re doing”™.

They can get you from blank screen to a functioning experiment very quickly. They can help build the boring but necessary pieces that make it safe to test a hypothesis. They can make it cheaper to be wrong.

What they cannot reliably do is hand you the special sauce: an insight that is not already obvious, already priced in, or possibly sitting somewhere in the training data. You still need to decide what is worth measuring, why the market might be wrong, and what would prove you wrong.

That may sound less exciting than an autonomous trading bot. But it is probably the actual opportunity. If it is suddenly cheap to build and test an idea, then the bottleneck moves back to having a good idea.

Next up: time to try designing my own peptide with Codex.

HIPAA-Compliant LLM Access Is Not That Hard

HIPAA-compliant LLM access really is not that hard.

I came to this conclusion after going fairly far down the wrong path.

It started with Kimi K3 and GLM-5.2. These open-weight models are getting close enough to proprietary frontier models that they need to be taken seriously.

My first thought was that healthcare companies could run models like these themselves and keep patient data inside their own environment.

That is an appealing idea. No protected health information sent to a model API. No dependence on a third-party inference service. A company controls the hardware, the model weights, the network, and the logs.

Then I looked at the hardware.

These are not small models. Kimi K3 has 2.8 trillion parameters, and the vLLM project describes an eight-NVIDIA-B300 deployment as its easiest way to run the model. GLM-5.2 has 753 billion parameters; NVIDIA’s current quantized release targets Blackwell hardware and supports runtimes such as vLLM and SGLang.

The exact footprint depends on the precision, context length, concurrency, and serving stack. That is the point. An organization considering this path needs to budget for more than a GPU server: high-speed GPU interconnects, storage for weights and logs, redundant infrastructure, monitoring, patching, model serving, and someone who can operate all of it. For serious production traffic, that can become a large infrastructure project quickly.

That does not make local inference impossible. For the right workload, volume, and organization, it may be a reasonable choice.

But it makes “we should run our own frontier model because HIPAA” a much more expensive sentence.

The local-model path has more than one vendor

The next option is a GPU cloud. Some providers will sign a business associate agreement (BAA), which is a necessary part of handling protected health information with a service provider.

But the details matter.

A BAA may cover specific compute, storage, and logging services rather than everything the vendor offers. The organization still needs to understand where prompts, responses, embeddings, backups, traces, and support data go. “The GPU is covered” is not the same as “the whole system is covered.”

That is real diligence, but it is also not unique to open models.

Then there is the more obvious question: what about the clouds most healthcare companies already use?

AWS lists Amazon Bedrock as HIPAA eligible. Microsoft offers a BAA for in-scope Azure services, and its Azure AI Foundry documentation describes its HIPAA compliance offering. Google supports HIPAA workloads through Vertex AI.

So a healthcare company can already get managed access to capable models from AWS, Microsoft, or Google without buying a GPU rack or operating an open-weight model.

The model is available. The work is deciding what to do with it.

HIPAA is a system property

A BAA does not make an application safe by itself. Neither does a local GPU server.

The company still needs appropriate access controls, encryption, audit logging, retention rules, and an architecture that keeps PHI inside covered services. It needs to know which users can ask which questions, which records the system can retrieve for them, and whether sensitive data is leaking into an observability tool or a debugging log.

Those are not optional details around the LLM. They are the system.

A useful healthcare assistant also needs to be more than a chat box pointed at a model. An end user should see an authenticated application that retrieves only the records they are allowed to access, shows where an answer came from, and makes it easy to correct or escalate an uncertain result.

Take prior authorization. An LLM could help assemble information from a patient record into a draft packet. That may save a person time. But somebody still needs to decide what information is relevant, validate that the draft is correct, submit it through the appropriate channel, and handle the exception when the case does not fit the usual pattern.

The same is true for chart summarization, intake, coding support, patient-message triage, and internal policy search. The question is not just whether a model can produce a plausible answer. It is whether the workflow makes a person faster without making a mistake harder to catch.

Start with the workflow

I suspect plenty of healthcare companies are treating HIPAA as the blocker when the larger issue is that they have not identified a narrow enough problem worth solving.

“Give our staff an LLM” is not a workflow.

“Help the prior-authorization team find the relevant clinical history, draft a packet, and flag missing information for review” is much closer. It gives the team something to evaluate: time saved, completeness, error rate, review burden, and the cases where the system should stop and ask for help.

Once that workflow is clear, the infrastructure decision becomes more practical.

A managed model service may be the right answer if it fits the company’s covered environment and the team wants to focus on the application. A locally hosted open model may be the right answer when the organization has unusually strict control requirements, enough sustained volume to justify the infrastructure, or a reason to operate the model as a core capability.

Neither option removes the need for careful design. Both can be part of a HIPAA-compliant system. Both can also be used carelessly.

The LLM is not really the hard part.

The hard part is picking a useful workflow, connecting the right data, evaluating the output, and deciding where a human needs to remain involved.

What healthcare workflow would you be comfortable giving an LLM access to today?

CRUD Is Getting Cheap. The Work Is Not.

A while ago, a brochure website was a meaningful software project. Somebody needed to lay out the pages, create navigation, make a contact form work, and get it all deployed. Website builders did not make a good website automatic, but they made that particular layer of work cheap enough that it stopped being the main thing most companies paid for.

Something similar is happening to CRUD applications.

A competent engineer with current tools can get surprisingly far, surprisingly quickly: a schema, basic APIs, forms, table views, search, permissions, validation, an admin screen, and a handful of ordinary integrations. LLMs help produce that code faster and make the usual implementation details less expensive to iterate on.

That is real progress. It is also easy to draw the wrong conclusion from it.

The fact that it is getting easier to build a system of record does not mean the business problem is solved. It means the database-shaped part of the problem is less scarce.

The valuable question was rarely just “where do we put the records?” It was “what should we do next, who needs to do it, and how do we know it worked?”

From recording work to improving work

CRUD is still necessary. Organizations need a place to record customers, jobs, invoices, inventory, cases, and the rest of the nouns that make up their work. They need people to be able to find and correct those records.

But a record is not an outcome.

A useful distinction is between a system of record and a system of action. The first stores what happened. The second helps decide what deserves attention, coordinates action across people and systems, and learns from the result.

System of record System of action
Stores customers, jobs, invoices, and cases Prioritizes work and moves it forward
Lets people enter, search, and update data Coordinates people, systems, and exceptions
Reports what happened Forecasts, recommends, and optimizes what to do next
Uses broadly reusable patterns Encodes domain-specific constraints and tradeoffs

The CRUD layer is often part of a system of action. It is just not usually the part that makes the system valuable.

The floor is moving to workflow

Take a customer-success tool. The commodity version has accounts, contacts, renewal dates, health-score fields, notes, and tasks. That is useful, and it is also a familiar application shape.

The harder version combines product usage, unresolved support issues, contract terms, champion turnover, and outcomes from similar accounts to answer a more useful question: which accounts need attention this week, and what action is most likely to change the outcome?

Then it needs to make that action practical. Perhaps a support issue needs escalation, a CSM needs a meeting, sales needs to be involved before a renewal date, and the team needs a shared view of what happened next. The value is not a nicer account page. It is reducing the chance that an important customer falls through the cracks.

This pattern appears everywhere. Field-service software can store work orders, technicians, addresses, and status updates. The differentiated work is scheduling and re-scheduling against technician skills, promised windows, parts availability, geography, overtime rules, uncertain job duration, and emergency calls. A credible schedule at 8:00 AM is not enough if the system cannot respond when a job takes twice as long as expected at 10:30.

That is workflow orchestration: the messy part involving handoffs, timing, exceptions, policy, and people. It is not glamorous, but it is where a lot of operational software earns its keep.

Not everything valuable is an LLM

LLMs are part of this shift, but “CRUD to AI” is too narrow a description.

LLMs are particularly useful when a workflow begins with unstructured information: an email, a document, a call transcript, an image, or a request written in normal language. They can help extract information, classify incoming work, summarize context, or give a person a natural-language interface to a system.

Other valuable systems may have no LLM in the critical path at all. They may use a rules engine, a forecast, a statistical process-control chart, a constraint solver, a simulation, or a carefully constructed report. Many systems will combine several of these approaches.

The common thread is not the model. The software does more than preserve a record of work. It helps make a better decision, execute it, and learn from the outcome.

Analytics turns data into a question worth answering

Consider revenue operations. A CRM stores leads, opportunities, stages, activity, and quotas. The useful analysis is often above that layer: pipeline coverage by segment, conversion rates between stages, typical cycle times, and the difference between a healthy-looking pipeline and one that is unlikely to close in time.

Those views support real decisions. Is a territory short on coverage? Is a segment converting differently? Does the organization need more sales capacity, a different territory design, or a different target? The implementation might be straightforward cohort analysis or a forecast based on historical data. It does not need to be generative AI to be valuable.

Product analytics has a similar trap. A dashboard can show that activation or retention moved. An experiment, with a clear metric and a credible comparison group, helps answer whether a product change caused the movement. That difference matters when deciding what to ship to everyone.

The system of record supplies the events. The analytical layer makes them useful for a decision.

Optimization makes tradeoffs explicit

Some of the highest-value software is not about generating text or predicting a label. It is about choosing among competing, constrained options.

A logistics application might store shipments, vehicles, drivers, stops, service windows, and delivery status. The difficult work is assigning loads and planning routes while respecting vehicle capacity, driver-hours rules, pickup timing, delivery promises, and cost. This is an optimization problem. There may be no chat interface and no LLM involved.

Inventory is another familiar example. A basic app can show that stock is low. A more useful system estimates demand and lead-time uncertainty, accounts for storage limits and the differing cost of stockouts, and recommends what to order, from whom, and when. It makes the tradeoff visible instead of leaving a person to infer it from a table of quantities.

Workforce scheduling has the same shape. Employee records, certifications, availability, and shifts are CRUD. Building a workable schedule means balancing coverage, labor rules, preferences, fairness, qualifications, and overtime. The value is a schedule that an operation can actually run.

Operations research, forecasting, and constraint solving have been doing this work for a long time. Cheaper application development does not replace them. It makes it more feasible to spend effort on the part that changes the outcome.

Reliable execution is part of the product

A recommendation that cannot be acted on is just another dashboard.

Useful systems need to connect to the places where work happens, create or route the next task, explain why an item was prioritized, and handle cases that do not fit the normal path. They need audit trails where the decision matters. They need safe fallbacks and a clear way for a person to take over.

This is especially important when an LLM is involved. A model can help read an invoice, summarize a case, or classify an incoming request. It should not turn uncertainty into an invisible decision. The system needs confidence thresholds, validation, exception queues, permissions, and a way to correct mistakes. Those are not incidental implementation details. They are what make automation usable in a real operation.

The same is true for non-AI logic. A routing optimizer needs to expose the constraints it used. A forecast needs to show when its assumptions no longer resemble reality. An approval workflow needs a path for the unusual case. Dependability is not separate from the product; it is part of the value proposition.

Start with the bottleneck, not the screen

For builders, the practical implication is simple: start with the recurring decision or bottleneck.

Ask what people are repeatedly deciding, what information they have to assemble to decide it, which constraints they are balancing, and what happens after they make the call. Then work backward to the data, integrations, analysis, and interface required.

That approach may still produce a CRUD application. Most useful systems need records. But the record pages become infrastructure for a more specific outcome: fewer missed renewals, better route utilization, more disciplined purchasing, faster resolution, or a decision that used to require several people and a spreadsheet.

LLMs have made the CRUD shell cheaper to produce. That should be good news. It lets teams spend more of their attention on the work that has always been difficult: understanding an operation well enough to remove delays, make tradeoffs explicit, and reliably move work forward.

LLMs Need Someone Who Knows the Domain

Claude, Codex, and the rest are useful debugging partners. They can suggest hypotheses quickly, explain unfamiliar systems, and keep an investigation moving when you are stuck.

They can also send you on a very convincing rabbit hole.

We ran into that with a client’s ASP.NET application. After it had been up for a while, the first request for a static JavaScript file could be very slow. Requests after that were fast. It was the kind of narrow, intermittent behavior that invites a long list of theories.

Claude’s initial diagnosis was that SSL certificate revocation checking was holding up the first request. That is a real thing worth knowing about, and it sounded plausible in the abstract. But the application was using a self-signed certificate. There was no certificate authority revocation check to perform. A small piece of domain knowledge ruled out a direction that otherwise could have consumed hours.

The problem was not that Claude mentioned certificate revocation. The problem would have been treating a confident, technically detailed answer as evidence.

Start with what the system is doing

Rather than follow the SSL theory, we tested the behavior we could observe. We read the assets directly from the filesystem and fetched the asset through the application. The pattern was consistent:

  1. Fetch an asset and the first request is slow.
  2. Fetch it again immediately and it is fast.
  3. Edit the file, then fetch it again, and the next request is slow again.

That is a much more useful description of the issue than “static JavaScript is slow.” The expensive path was associated with first access to changed file content. It was not ordinary request handling, and it did not fit the TLS explanation.

The experiment strongly pointed to endpoint scanning. SentinelOne was running in that environment and was the most likely cause: changed content was likely being scanned on its first access, while the next read benefited from the result already being available. We did not treat that as a definitive vendor-level attribution, but it fit the observed behavior far better than revocation checking did.

Plausible is not proven

LLMs are especially good at producing plausible explanations. They have seen the vocabulary around a symptom, and they can connect it to a real mechanism. That is useful for generating a list of things to investigate.

But a diagnosis has to survive the details of the actual system:

  • Does the proposed mechanism exist in this deployment?
  • Does it explain the timing and repeatability of the symptom?
  • What inexpensive test could distinguish it from the other hypotheses?
  • What observation would prove it wrong?

A self-signed certificate was enough to make us stop and question the revocation theory. The cold-read, warm-read, and modified-file test gave us a better hypothesis to pursue. Neither step required an encyclopedic knowledge of every possible cause. They required knowing enough to check the assumptions and to design a small experiment.

Use the model as a partner, not an authority

Claude still helped with the investigation. The right use was not to ask it for the answer and implement the first response. It was to use it as a partner while we compared theories against the environment and the measurements.

A practical debugging loop looks like this:

  1. State the observation precisely, including what changes between a slow request and a fast one.
  2. Ask the model for competing hypotheses and a test that would separate each one.
  3. Check its assumptions against the architecture, configuration, and operational environment.
  4. Run the smallest useful experiment.
  5. Feed the result back in and repeat.

This is also why domain expertise still matters when using LLMs. If you cannot tell whether an answer fits the system you are operating, confidence and detail are easy to mistake for correctness. Bring in someone who knows the domain, or slow down enough to validate the model’s premises before chasing its conclusion.

The model can make a good investigator faster. It cannot replace the judgment needed to decide whether a theory belongs in the investigation at all.