AI-Assisted End-to-End Product Development A solo developer built Fincher, a social app for discussing movies and TV shows, using AI across the full product-development lifecycle from strategy to infrastructure. The project, started in January 2025, is now production-ready and approaching launch, demonstrating that AI compresses time but does not eliminate the need for rigorous engineering discipline. Shipping a software product alone is becoming common. Shipping one that holds up is not. The first fact is genuinely new, and it is not the interesting part. The interesting part is what happens next. The product meets its first traffic spike and falls over. The first security incident finds no posture, only panic. The first cloud bill lands at a number nobody modelled, because every feature was built as if compute were free. The codebase reaches month six and can no longer be changed safely, because it was generated rather than designed. Shipping got easy. Shipping something that survives did not. The distance between the two is the entire discipline of engineering, which did not go anywhere. Engineers are failing at this too, in a quieter way, and it is not a prompting problem. AI is adopted for code completion and left there: one discipline out of the dozen that product development comprises. Or it is adopted everywhere and trusted everywhere, which fails differently: the research step disappears, because the model produces something that reads like research; options go unenumerated, because the model produced one and it looked good; numbers go unverified, because they arrived formatted in a table. The output looks like the work was done, so the work stops being done. Both failures share a root: treating AI as either a typing accelerator or an oracle, when it is neither. What it actually is: the collapse of the constraint that most reliably kept product development a team activity. Building a complete product strategy, UX, architecture, backend, mobile, infrastructure, launch was never beyond one person's ability. It was beyond one person's hours. AI compressed it enough to change what one person can attempt. What replaced it is a different constraint, and everything that follows is about it: The work did not shrink. The time did. Every discipline still applies. All of it still has to be done, by you, with the same rigor as before. What changed is that each loop costs an hour instead of a week. Nothing was removed from the job. Nothing became safe to delegate blindly. The schedule compressed; the obligations did not. A useful audit: list the steps you performed on your last pre-AI project. Mark the ones you no longer perform. Each mark is a defect, not a saving. This methodology is not theoretical. It was extracted from a year and a half of building Fincher — a social app for discussing movies and TV shows — alone, in the hours around a full-time job, using AI across the product-development lifecycle, from product strategy and UX through architecture, implementation, and infrastructure. First commit January 2025, one contributor throughout, now production-ready and approaching public launch. What follows is organized as rules. Each is stated generally, because each applies to any product, in any domain, on any stack. Rule: anything that does not survive a model generation change was tooling, not methodology. Expect to discard it, and do not mourn it. In 2024 I ran a rigid document pipeline. Every feature began with a technical specification written to a template; the specification produced a templated implementation plan; the plan produced code. Nothing was built without both documents. That pipeline no longer exists in my practice. The documents do. The distinction matters. The rigid pre-implementation pipeline was never methodology; it was a prosthetic for weak models that forgot context, invented APIs, lost the thread across a session. When the models improved, the prosthetic came off. The definition work still happens with the same precision, and it is still written down, but in conversation first, with the document produced at the end as a record rather than at the beginning as a leash. The records themselves decisions, rejected options, the why remain central; the research discipline and the ability to interrogate old decisions both depend on them. This will happen to whatever process you are running right now. The test that matters is generational survival, and it applies to everything in this field, including this document. I have run this practice across three generations of tooling; the scaffolding was rebuilt every time. What follows is what did not move. 4.1 — Decision authority never transfers. The model proposes. You dispose. Without exception: including when it is right, including when you are tired, including when the decision is small. Every other rule here assumes someone is exercising this one. 4.2 — Nothing is built from your own head alone. Every meaningful decision is researched, compared, and sourced before it is made. Treat the model as a research instrument, not an oracle: it can survey a landscape of published studies, prior art, competitor behavior, and public user complaints faster than any human, and it should be doing so constantly. A later section develops this as a product discipline in its own right. 4.3 — Enumerate before you commit. Never accept the first proposal. Require the alternatives. Require the trade-offs of each. Then argue. A decision made without enumerated alternatives is not a decision; it is an acceptance. The rejected options belong in the record with the reasons for their rejection. They are as load-bearing as the choice, because they are what makes the choice auditable later, by you or by anyone. 4.4 — Demand sources, and form your verdict before reading theirs. Require citations, then read them. Not for diligence, but because the primary material contains what the summary omitted, and some of your best insights will come from the sources rather than the synthesis. Reach your own conclusion before reading the model's; divergence is signal, either about the problem or about how far to trust this particular answer. A model that must cite a real page has less room to guess. 4.5 — Cross-examine across models, and shuttle until verdict. One model's confident answer is a hypothesis. A single second opinion is barely better: model B critiques model A's answer as cheaply and confidently as A produced it. The practice that works is a shuttle: carry B's objections back to A, carry A's rebuttal back to B, round after round, until the disagreement is resolved or fully mapped. The termination condition and the referee are both load-bearing. A shuttle without a human judging each round degenerates into two models politely negotiating fiction. The models will not always converge; when they don't, the residue of the exchange is exactly what you need to judge the question yourself, which was always the destination anyway. I ran every significant decision through this for the life of the project, with distinct roles: one model owning product and strategy, another owning architecture and implementation, each standing as adversarial reviewer of the other. The disagreements were worth more than the agreements, and the late rounds were worth more than the first. 4.6 — Four pillars gate every feature, regardless of size. Security, performance, cost, scalability: evaluated at the front of every feature, not reviewed at the end of the project. Including trivial features. Especially trivial features. 4.7 — Code is the cheap part. Design adherence is the expensive part. The one place trust is extended without reservation: the literal lines of what was asked for. I do not care how the model writes a loop, inside the boundaries of the request and the specified design. I care about one thing: does the implementation conform to that design? The trust is scoped, not blanket: what the model does outside the request is a different problem entirely, and a later section treats it as the hazard it is. Within the scope, a standing element of the loop is auditing the codebase against its own stated principles and refactoring the drift. The design is the asset. The code is its current expression. Rule: before anything is designed a product, a feature, a screen, an element , the evidence is gathered. Design starts from data, not from taste. This is where AI changes product development most and where it is used least. Engineers use models to build; almost nobody uses them to find out . Yet the model's most underrated capability is surveying evidence at a scale no individual could: studies, forum threads, app reviews, competitor behavior, prior art, gathered and sourced in minutes. Three levels of the same discipline: Product direction comes from documented pain, not intuition. Fincher's differentiation was not invented; it was mined. Before the first commit, I had the model systematically collect and categorize what users of the incumbent apps publicly complain about, across Reddit, reviews, and forums, and rank the complaints by frequency and intensity. The model accelerated the collection and the categorization; I kept the source links, read the underlying threads and reviews myself, and treated the resulting patterns as directional evidence, not statistics. The product thesis fell out of the list: broken TV-show support, language barriers, weak search, buried social features. Every one became a pillar of the product. I did not have to guess what people wanted. They had been saying it, in public, for years; nobody had collected it. Technical selections are surveys, not preferences. Choosing a search engine, a feed backend, an auth approach: each was a commissioned comparison. Enumerate the candidates, price each at three scales, evaluate against the four pillars, cite sources, then argue. An engineer who picks infrastructure by familiarity is gambling that their past happens to fit this product. The survey costs an afternoon and makes the gamble visible. Even the interface is evidence-based. This is the level that surprises people. For nearly every element of every screen color, contrast, shape, position, spacing, animation duration I had the model find published UX research, design-system guidance, and comparable implementations in successful apps, and I read what it found before deciding. Most of the interface is evidence-informed rather than taste-driven. Taste has a place, breaking ties and setting identity, but it goes last, after the evidence has narrowed the field. Designing by what looks good to you quietly optimizes the product for an audience of one. Two mechanics make all of this work. First, require sources, then actually read them : the summaries are the map, but the insights are in the terrain, and a model forced to cite real pages has less room to guess. Second, the research artifact outlives the decision : the collected evidence, the comparison, the rejected options and why, written down. A year later, when something looks wrong, the answer to why is it this way is in that document instead of lost. This is the central law of working with these systems. Everything else in this methodology is commentary on it. The model amplifies the frame you bring. It will rarely examine it — least of all when you are most certain. Models can push back on a premise; occasionally they do. But in practice they overwhelmingly answer the question they are given, and the tendency is strongest exactly when it matters most: when you arrive with a direction already chosen: specific, confident, moving. The model does not resist. It helps: competently, agreeably, indefinitely, refining your approach version after version, and it rarely escalates to we should revisit the premise once the conversation is moving inside a confident frame. This would be harmless if engineers arrived at problems without preconceptions. Nobody does. You read a requirement and a solution shape forms in your head before you finish the sentence. That is what experience is. Pre-AI, a wrong solution shape had a natural predator: friction. You got tired, you got stuck, the wrongness surfaced through resistance, and you stepped back. AI removes the predator. The model supplies unlimited competent momentum in whatever direction you are already pointed, so a wrong frame no longer runs out of steam. It compounds. I did this, and the frame that caught me was not a careless one. Building a feature to show which of the people you follow have rated a given film, I knew the ideal shape this is relational work , but the product was committed to a document store without joins, and I had correctly internalized that constraint months earlier. So the design conversation started inside it: walk the following list, check each person's rating for the film. Reasonable, shipped, working, until I ran the numbers at target scale, where following over a thousand accounts is normal, and the shape collapsed: thousands of sequential reads, tens of seconds, unusable. Here is the part that matters. The optimization discussion that followed, my ideas and the model's alike, stayed entirely inside the frame: cap the list, batch the reads, parallelize, paginate. Every one of them a better way to do a fundamentally wrong thing, and the model refined each with total competence, because the frame was in the premises I had supplied, and it cannot be counted on to audit premises. Neither of us said the only thing worth saying: stop optimizing this query. What fixed it was abandoning the query frame: not the platform constraint, which was real, but my formulation on top of it. I restated the root problem with no solution vocabulary in it given one film, which of my friends have an opinion about it? and ran the invariants against that instead: research the shapes, enumerate them, compare at target scale. The winning design inverted the query direction completely, from social-graph-sized reads down to a small bounded entity query, from tens of seconds to a fraction of one, and it lived comfortably inside the same document store. The constraint was never the problem. My formulation was. And it could not have been reached by improving my idea, only by abandoning it. So the practical question is not "how do I avoid bad frames" you can't; they arrive with your experience but "how do I detect one from inside it." The tell is machinery. When a feature keeps accumulating mechanism another special case, another sync path, another cache layer , the usual reading is this problem is hard . The better reading is this frame is wrong . I have watched an elaborate re-sync mechanism dissolve because two concepts I had fused were actually independent; watched a three-layer cache with distributed locks reduce to plain transactions, taking a standing monthly bill with it; watched a confirmation-and-soft-delete flow vanish when the "edge case" it guarded became a valid state. In each instance the model had been answering my question correctly the whole time. The question was wrong. The rule: when machinery accumulates, stop improving the answer. Re-state the root problem with no solution vocabulary, and make the model enumerate against that. This is enumerate-before-commit applied one level above implementation: enumerate the framings, not just the options. It is the highest-leverage moment in the practice, it is invisible from inside the work, and it is the move the model cannot be relied upon to make for you: it does not want a different question. You have to. This is also why an inexperienced engineer with identical tools ships a worse system, and it has nothing to do with prompting. The model may produce a design that serves ten thousand users beautifully and comes apart at a million. Not because the model failed, but because nobody in the conversation knew a different question existed. That knowledge is the expertise. It is not in the prompt, and it cannot be fully put there. Rule: price every feature at target scale before implementing it, and treat the price as a design input, not an afterthought to be discovered on the first invoice. The sequence matters. For any feature with a per-use cost an LLM call, a third-party API, a fan-out write , the first question is not how do I build this but can this exist at all at the scale I intend? A feasibility gate, run before design begins: model the cost at three points on the usage curve current, plausible, aspirational and only then decide whether and how to build. I ran this gate on a feature that classifies user posts with an LLM, at roughly a hundred tokens per classification. The model produced the analysis instantly: a clean projection table, per-post token math, monthly totals at each scale. It priced the LLM at $0.15 per thousand tokens: 10,000 posts a month, $150; a million posts, $15,000. The published price, one page away, is $0.15 per million tokens. Every figure was wrong by three orders of magnitude; the true cost of the first row is about fifteen cents. Weigh both failure directions. Trust the number and you budget $15,000 a month for a $15 feature. Or, likelier and far worse, you read $15,000, conclude the feature is unaffordable, and silently never build it. No bug, no crash, no red test. A correct product decision destroyed by a hallucinated decimal point, leaving no artifact to audit. The obvious objection is to skip the model and build the projection by hand. But that discards the real gain: the model produces the entire structure of the analysis in seconds; the rule protects the inputs. Careful reading will not catch this; the output reads beautifully. That is the problem. A rule catches it: Any figure that will influence a decision is verified against the primary source before the decision is made. Every figure. Every time. With verified numbers, the gate becomes a design instrument. The corrected analysis showed the feature was viable, and the same pass produced the constraints that keep it viable at scale: cache classifications by content hash so identical text is never paid for twice, skip trivially short posts, batch the calls, use the cheapest model that clears the accuracy bar. The cost model did not just approve the feature. It shaped it. The requirement is never "cheapest." It is the cheapest implementation that does not compromise security, performance, or scalability. That joint constraint is what produces good architecture. An unlimited budget absorbs bad decisions silently; a hard budget surfaces them as line items. Engineers who have spent their careers inside corporate budgets have usually never felt this forcing function, and it shows, industry-wide: So much cloud architecture is accidentally expensive because nobody in the room was personally paying for it. I was. The project has no funding or revenue; infrastructure comes out of pocket. Take translation. The obvious build detects the language of every post at creation, translates everything into everyone's language, caches it all: clean, fast, financially unserious at volume. What shipped: detection by an open-source detector running locally inside the serverless function , zero API cost against a hosted endpoint billing per call; translation strictly on demand one post, one tap, one translation ; results cached server-side by content version, paid exactly once. None of this is clever. It is what happens when every line item requires a personal signature, which, for a solo product, it does. The methodology simply refuses to wait for the invoice to make that true. The invariants named the gate: security, performance, cost, scalability, evaluated at the front of every feature. Naming the pillars is easy; every engineering blog names them. What makes them operative is that each is a fixed set of questions forced into the conversation on every feature, without exception, before implementation is discussed , because the model answers whichever questions are asked, and these are the questions that go unasked. Security. What is this feature's attack surface? What can a hostile client send not the UI, the client: the UI is a suggestion, the API is the contract ? What do the backend rules permit that the interface merely declines to offer? Where does user content get executed, rendered, or interpolated? What is exposed if this datastore leaks tomorrow? For a social product, this interrogation runs on every feature that touches input, which is nearly all of them. Performance. What is the perceived latency, the time between the user's gesture and the interface responding, not the backend number? What is optimistic and what waits for confirmation? What does this screen cost on a low-end Android device, not the flagship in the emulator? What happens on a degraded network, and on no network? The model, left alone, builds correct-then-responsive. The order must be imposed. Cost. The full protocol of the previous section: the curve at three scales, verified prices, the cache/skip/batch pass, the cheapest design that holds the other pillars. Scalability. The question is always the same: what is N here, and what happens to it at a million users? Every feature has an N: reads per view, writes per action, fan-out per event. Find it, multiply it, and watch for designs where N grows with the social graph: those are the ones that pass every test and die in production. The friends-ratings query was exactly this. The best outcomes are not where N scales well but where the reframe makes N stop existing. Two properties make the protocol work. It is unconditional : trivial features are interrogated too, because they are where unexamined defaults accumulate. And it is front-loaded : these questions shape the design; asked afterward they can only audit it. The research discipline, the cost gate, and these four protocols are the same instruction viewed from different angles: the model brings competence; you bring the questions. The questions are the methodology. 9.1 — Repeated failure is information about your feedback loop, not about the problem. The frame problem has a sibling that is easy to confuse with it, and the confusion is expensive. There, the question is wrong and the answers are correct. Here, the question is right, but the signal you are steering by is false. Every debugging loop steers by instruments: console output, stack traces, logs, tests. The model steers by the same instruments, one step removed: it only knows what you paste. When an instrument silently lies, you and the model go blind together, and neither can detect it, because you are each other's only cross-check and you share the same corrupted feed. I lost a long stretch of development to this once. An authentication flow froze the app from one specific screen while the identical flow worked everywhere else, and fix after fix failed. Not because the theories were bad, but because the crash-reporting SDK had replaced the framework's default error handler and was silently swallowing the very errors that would have located the bug. The console showed nothing wrong. Nothing wrong and nothing reported are indistinguishable on a corrupted channel, and every failed fix generated a plausible next theory rather than the correct conclusion: stop fixing, and audit the instruments. Once error visibility was rebuilt, the bug fell almost immediately. The conclusion I recorded at the time: error visibility is load-bearing infrastructure. The general rule: consecutive failed attempts are not a streak of bad luck to be broken by a better next attempt; they are evidence about the loop itself. Two consecutive failures, ask which is wrong: the question, or the signal. Ask a model for the next fix and you will always receive one: plausible, confident, and downstream of the same false signal as the last three. It cannot audit your instruments; it lives entirely inside what they report. A related discipline follows: a fix that merely silences the symptom compiles clean, passes the analyzer, stops the error has established nothing except that the signal went quiet, and a signal you quieted without understanding is now one more corrupted instrument in your loop. 9.2 — Contain the model's blast radius. The model's most dangerous property is not that it errs. It is that it extends : asked for one change, it improves adjacent things. It rebalances a shared value to compensate, renames something it finds inconsistent, "fixes" a structure it judges suboptimal. Each improvement is locally defensible. The aggregate is a codebase where you no longer know what changed, and after discovering one uninvited change, every change in the session becomes suspect. Re-establishing that confidence costs more than the session saved. Two disciplines contain it. Scope is a hard boundary, not a suggestion. A change requested for one mode, one state, one component touches nothing shared with anything outside that scope, even when the requested change appears to justify "rebalancing" something adjacent. The judgment that an adjacent thing now needs adjusting belongs to you; the model surfaces it and asks. Enforce this in the standing rules, and treat violations as seriously as bugs, because that is what they are: unreviewed changes to code you believed untouched. Existing structure is presumed intentional until established otherwise. A codebase with a year of iteration carries invariants written down nowhere: ordering-sensitive route tables, deliberately duplicated logic, a "wrong-looking" placement that is load-bearing. The model has no access to that history. It will read the surface, conclude the code is wrong, and confidently correct it, and the cost of its confidence lands entirely on you, usually weeks later. The first question when something looks suboptimal is why is it this way , not how do I improve it . This applies to the model's judgments and, more uncomfortably, to yours: the answer to why is sometimes in a decision you made a year ago for reasons that still hold. Rule: when you correct the model twice for the same thing, stop correcting and start encoding. Corrections are expensive when repeated and nearly free when written down. Two practices compound here. Standing rules. Every recurring correction becomes a persistent instruction the model reads at the start of every session: scope boundaries, approval requirements, architectural constraints, review procedures. Preparing a backend for production, I reviewed every serverless function individually and kept finding the same defects: a retry flag missing here, the wrong error type there, a counter a platform retry would silently inflate. By the fifth function the review had a fixed shape; by the seventh the shape was a standing checklist the model applies to any function on request. A review that took an afternoon per function at the start took minutes at the end, at higher consistency than I could sustain by hand. Conformance audits. The mirror-image practice: periodically, the model reviews the codebase against its own recorded rules the layer boundaries, the naming conventions, the architectural decisions in the documents and reports the drift. Drift is constant and blameless; sessions are long, context fades, and yesterday's exception quietly becomes today's pattern. The audit is cheap, and it converts the design documents from aspiration into enforcement. Together these form a ratchet: judgment exercised once becomes a rule; rules accumulate; the model executes them tirelessly. What the ratchet cannot do is turn itself. The next section is about why. I maintain persistent memory files for the AI I work with: standing rules it reads at the start of every session. Three of them encode the same principle at escalating specificity: The third file exists because the first two did not bind. Mid-refactor, with both earlier rules in persistent memory, a routing change produced a greedy matcher that captured sibling routes. Rather than stopping, the model unilaterally renamed a public URL scheme: deep links, shared links, notifications, every URL anyone had ever sent. It had the rule. It was inside the exact situation the rule described. It proceeded anyway. So a third rule was written, narrower still: approval for a plan is not approval for whatever surfaces during the plan. This is the finding. You can write the rules down. You can hand them to the model verbatim, in persistent memory, re-read at every session start. It will still take a human to recognize, in the moment, that this is one of the moments the rule describes. That recognition does not transfer with the rule. It does not live in a configuration file. The rules are the residue of the judgment. They are not the judgment. Which is why copying the artifacts of this methodology these rules, the checklists, the document formats, the prompts will not reproduce its results. Nothing has been withheld. The artifacts are simply what the judgment left behind on its way past. The platform this was extracted from is designed for a scale it has not yet served. Every feature was architected and reviewed against a million-user target. That is design, verified by arithmetic and review, not yet by traffic. The distinction is worth keeping sharp, because engineers who blur it ship systems that fail at the first inflection point, and because designing correctly for load you have not yet met is, in the end, the entire subject of this methodology. This is one engineer's extraction from one sustained build. It is a methodology, not a study, and parts of it will age: the scaffolding of 2024 is already archaeology, and the substrate test from early in this document exists precisely because permanence cannot be pretended. These seven invariants have survived three generations of tooling, and the test should be re-applied, to this document included, every year. The claim I expect to survive is narrower and harder to dislodge: AI collapsed the cost of every step of product development and changed the value of none of them. The durable advantage is not producing more output. It is exercising more judgment, across more disciplines, in less time. Everything here follows from that. The work did not shrink. The time did. Pedro Estevão is a software engineer with sixteen years of experience across the full cycle of product development, based in Dallas, Texas. Fincher is his independent solo project.