Skip to content

Solo Founder Product Engineering Handbook / Chapter 22

Architecture for Learning

Design pre-PMF architecture around learning speed, reversibility, and enough reliability to protect trust.

A System That Can Answer Back

The previous chapter chose a deliberately ordinary stack for the first paid agency pilot. That decision removed unnecessary technical bets. It did not yet produce an architecture.

Architecture begins when the founder decides what must remain true as real work passes through the system. An agency uploads a campaign export. The product turns it into a client-ready explanation. The founder reviews the draft. The agency receives it. Later, someone asks why a number looks wrong. Can the founder follow that path, see what happened, repair it, and learn whether the product was useful?

Before product-market fit, this is the standard that matters. The system has to deliver one narrow promise, reveal user and system behavior, survive ordinary mistakes, and remain cheap to change. A design that handles vast imaginary traffic but hides a failed pilot is premature. A fast prototype that loses customer state produces untrustworthy evidence.

The target is neither “quick and dirty” nor “ready for millions.” It is a small system that can answer back.

A simple bridge carries early users from idea and problem to first value moment. Four pillars support the bridge: deploy safely, observe behavior, recover data, and change quickly. Optional future reinforcements for scale, specialized services, and advanced automation wait above the bridge.
Pre-PMF architecture should carry early users to value while supporting safe deploys, behavior observation, recovery, and fast change. Reinforcements can arrive when evidence demands them.

Start with the Promise, Not the Components

Keep following the fictional agency-reporting pilot. Its first promise is precise: upload one supported campaign export and receive a reviewed, client-ready explanation of important changes. The product question is whether an agency will trust that explanation, use it with a client, and return the following week.

That promise sets the architecture’s boundary. The first system needs an agency account, an upload, a parser, a stored draft, a review step, delivery, and enough history to investigate the result. It does not yet need live advertising-platform integrations, client portals, elaborate team roles, real-time dashboards, or full-text search across years of reports. Those may become sensible products. They are not required to test this one.

Now write the one-page blueprint. In this pilot it might say:

  • Value moment: an agency operator receives an accurate draft they are willing to send to a client.
  • Evidence: uploads completed, drafts approved, corrections made, reports delivered, agencies returning, and pilots renewed.
  • Core records: agency, user, upload, processing job, report version, delivery attempt, and support note.
  • System shape: one application, one relational database, managed file storage, one background worker, and one deploy path.
  • Outside boundaries: mature authentication, transactional email, error tracking, and the model used to draft explanations.
  • Founder controls: inspect an account, compare input with output, edit a draft, retry a job, resend a report, and record a manual correction.
  • Recovery: retain the original upload, back up core records, restore them in a test, and make deploy rollback routine.
  • Deliberate omissions: live integrations, automated billing, client access, advanced permissions, custom themes, and search.

This is more useful than a box-and-arrow picture with every possible service. It names what the system promises, what it records, where it depends on others, how the founder operates it, and what has intentionally been left out. Each line can change when evidence changes.

Let One Report Reveal the Architecture

Suppose an operator uploads campaign-changes.csv. The request should authenticate the user, verify that the agency may create the report, store the original file, create an upload record, and enqueue processing. It should then return a visible status rather than hold the browser open while parsing, drafting, and delivery occur.

At this point the system has not merely “sent something to a queue.” It has created customer state. The upload needs an identity and a small, explicit lifecycle:

  1. received: the original file is durable and belongs to an agency.
  2. processing: a worker is parsing or generating the draft.
  3. needs_review: a versioned draft and its source facts are ready for inspection.
  4. approved: the founder has accepted a particular version for delivery.
  5. delivering: an attempt is in progress.
  6. sent: the provider has accepted the message for delivery and the attempt is recorded.
  7. failed: the current step stopped with a visible reason and a permitted next action.

These names are not ceremony. They prevent a vague “done” flag from erasing the difference between a malformed file, a model timeout, an unreviewed draft, and an email provider rejection. The lifecycle gives the UI something honest to show, the admin view something useful to inspect, and the founder a place from which to resume.

The original upload should remain unchanged. Parsed facts can be derived again. Drafts should be versioned because a founder correction is both customer work and product evidence. Delivery attempts should refer to the approved version so a retry cannot quietly send newer, unreviewed content. A worker retry should either continue safely or recognize that the step has already completed; it should not create a second report because a response was lost.

Follow the report far enough and the useful internal boundaries become obvious. Account ownership decides who may see the data. Ingestion turns a stored file into campaign facts. Drafting turns those facts into an explanation. Review creates an approved version. Delivery sends that version. Evidence records the behavior needed to judge the pilot.

Those boundaries can live inside one application. They do not require network services, separate repositories, or a message bus. Their purpose is to keep a product change local and a failure intelligible. If drafting changes, login should not change with it. If the email provider changes, the product should retain its concept of a report and its delivery history. If the agency’s vocabulary changes, the database should express that vocabulary rather than a vendor’s account model.

Keep the Whole Product in Reach

For one founder, a monolith or modular monolith is usually the clearest starting shape: one codebase, one primary database, one release, and one place to trace the user’s path. Local development is simpler, most records can change within a single transaction boundary, and a support question does not require reconstructing a conversation among services.

“Monolith” does not mean a single tangled module. The report lifecycle still deserves explicit ownership and interfaces. Authentication should not be scattered through drafting logic. Provider payloads should stop at their boundaries. Background jobs need named inputs and outcomes. Admin operations should call the same domain rules as customer-facing operations instead of mutating production tables through improvised scripts.

Nor is the monolith a promise never to extract anything. It is a refusal to guess where extraction will be necessary. If imports later consume most compute, the worker can scale or move without first separating accounts and reports. If agencies retain but demand live data, an integration boundary can grow from the existing ingestion path. If search becomes central after hundreds of reports accumulate, it can be added against a product model that has already survived real use.

The architecture keeps those options available through ordinary boundaries and usable data, not through empty abstractions built in anticipation of them.

Managed Does Not Mean Invisible

Managed infrastructure belongs here when it removes routine work without taking the product’s meaning away. A managed relational database, object storage, authentication, hosting, and email delivery can buy back days that would otherwise go to patching and operations. Error tracking and a modest event tool can make failures and usage visible without a home-grown observability platform.

The founder still owns every customer promise that crosses those services. For each dependency, the blueprint should answer a few concrete questions in prose: What enters it? What comes back? Which identifier connects the provider’s state to the product’s record? What happens during an outage? Can the relevant data be exported? Which provider assumption would be painful to undo?

Containment should be proportional. Email delivery needs a small boundary because the product’s report and delivery history should outlive one provider. Building a universal messaging framework would be waste. Authentication needs a clear mapping between external identity and the product’s user and agency records. Recreating authentication to avoid all dependence would be a larger and more dangerous distraction.

Some services do not belong in the first blueprint at all. The pilot invoices manually, so payment code has no job yet. The founder can find one agency’s recent reports through the admin view, so a search service would add another index and another failure mode without improving the test. Feature flags may begin as a small set of controlled configuration values for unfinished paths; a comprehensive experimentation platform would imply more traffic and variation than the product has earned.

This is architectural restraint in practice: use a managed capability when its boundary is understood, and omit it when the workflow does not yet need it.

Failures Should Leave Evidence

The happy path proves surprisingly little about an early architecture. The design becomes honest when the founder follows three ordinary failures.

First, an agency uploads a malformed export. The parser must reject it without losing the original, explain enough for the founder to diagnose the mismatch, and allow a corrected file to become a new attempt. If malformed inputs are common, that is product evidence: guided export instructions, a broader parser, or an integration may deserve attention.

Second, report generation times out after the parser succeeds. The job should keep the parsed facts, record the failed step, and permit a retry without repeating successful work. The founder should be able to complete the draft manually if the customer promise cannot wait. Corrections and manual completions should be visible; otherwise the apparent software margin hides a service business.

Third, email delivery fails after approval. The approved report must remain intact. A delivery attempt records the provider response and can be retried or handled through a manual fallback. The product should not regenerate the report merely because transport failed.

Useful observability falls out of these cases. Product events answer whether operators uploaded, reviewed, delivered, and returned. Structured job records answer what work is waiting or failed. Error tracking exposes unexpected faults. Logs connect a request, upload, job, report version, and delivery attempt without placing sensitive file contents into a general log stream. Support notes preserve the customer context that machine events cannot explain.

The founder needs one protected admin surface that brings this state together. It does not have to be pretty. It does have to answer: What happened to this agency’s report? Which step failed? What did the customer receive? Can I retry, repair, or explain it safely? An admin page that merely lists database rows is less useful than a narrow view organized around those support decisions.

Reliability Protects the Experiment

Early reliability is not an imitation of a large company’s platform. It is protection for the evidence the product is trying to earn.

A repeatable deploy and a practiced rollback keep a small product change from consuming the week. Database migrations should be reviewable, preceded by a current backup, and designed so application and schema changes can coexist long enough to recover from a bad release. Destructive cleanup can wait until the new path has proved itself.

Backups matter only when restoration is understood. The founder should restore a recent backup somewhere safe, confirm that core records and relationships are present, and know how stored files relate to them. An unchecked “backup enabled” badge is a vendor setting, not a recovery plan.

Access must match the data’s sensitivity from the first real user. The application needs clear agency ownership, protected founder operations, appropriately scoped credentials, and secrets kept out of source and logs. A campaign report does not require the same controls as payroll or medical data, but “pre-PMF” does not reduce the harm of exposing a customer’s information.

Alerts should point to situations in which the founder can act: repeated failed imports, a queue that has stopped advancing, undelivered approved reports, a backup failure, or a surge in user-facing errors. Page-level infrastructure telemetry can grow when it explains a real failure. Noise that wakes the founder but does not change a decision is another operating burden.

The baseline is sufficient when a tired founder can make a small deploy, see whether it worked, find a customer’s state, repair an ordinary failure, and restore the product’s core records. Higher-risk products must raise that bar before inviting users, not after an incident supplies the motivation.

Spend Reversibility Where It Matters

Not every architectural choice deserves the same deliberation. Copy, workflow order, manual approval, and the first set of product events are usually cheap to revise. A hosting provider or job runner may require a focused migration. The agency, ownership, report-version, and permission models reach much deeper because customer state and promises accumulate around them.

Move quickly on the first group. Give the second group clean boundaries and usable exports. Slow down on the third long enough to name what the product believes: who owns a report, which version was delivered, what may be changed, and what history must remain trustworthy.

Reversibility is not the ability to replace every component effortlessly. That goal would bury the pilot in indirection. It is the ability to change an experimental choice without corrupting durable customer truth. Keeping original uploads, versioning drafts, separating approval from delivery, and recording provider identifiers all serve that purpose. So does refusing to publish an API, promise instant automation, or encode complex team permissions before the evidence requires them.

Write one revisit trigger beside each deliberate omission. Add live integrations when retained agencies repeatedly identify CSV handling as the obstacle to continued use. Add client access when agencies are already forwarding reports and need a trustworthy shared history. Scale processing when measured queue delay threatens the promised delivery time. The trigger should describe pressure in the product, not restlessness in the founder.

Pressure-Test the Blueprint

Before building, take one report through the blueprint twice. On the first pass, let everything succeed. Name the record created at each step, the event that marks useful behavior, and the founder action that advances the work.

On the second pass, make the file malformed, the worker time out, the deploy introduce a bad migration, and delivery fail. Do not solve every imaginable disaster. Ask whether the system preserves customer truth, exposes the current state, and gives one operator a safe next action.

Then read the blueprint as if the first pilot has just sent a terse support message at 2 a.m.: “The report is wrong. What did my client receive?” If answering requires guessing across logs, provider dashboards, database consoles, and local files, the architecture has hidden the product from its only operator. If the answer is available but the product still cannot deliver a trustworthy report, the architecture has protected operations while missing value. Both sides matter.

The finished blueprint should fit on one page. It should name the promise, evidence, records, state transitions, external boundaries, founder controls, recovery path, expensive decisions, and deliberate omissions. It should be narrow enough to revise after the next agency conversation and strong enough to face a real user honestly.

Once that shape is clear, the next question is no longer architectural. It is a question of ownership: which capabilities inside the blueprint deserve to be built, which should be bought or borrowed, which can be performed manually, and which should wait.