# Graphlit: full text > Draw your system on a whiteboard. Graphlit turns it into a typed architecture graph, builds the app against it, and proves — commit after commit — that the code still matches the drawing. Every article published at https://graphlit.co/blog, in full, as one document. Each is also available on its own at `https://graphlit.co/blog//md`. **Generated:** 2026-08-23 · **Articles:** 17 You may quote from this with attribution to the source URL given under each article. Figures and claims are the ones we can defend; where something is not yet true of the product, the text says so rather than omitting it. --- ## Contents 1. [Why AI keeps breaking features that already worked](https://graphlit.co/blog/why-ai-breaks-working-features) 2. [Sketch-to-code tools compared: tldraw, Uizard, and more](https://graphlit.co/blog/sketch-to-code-tools-compared) 3. [AI app builders compared: v0, Bolt, Lovable, Replit](https://graphlit.co/blog/ai-app-builders-compared) 4. [Why AI-generated apps fall apart in week three](https://graphlit.co/blog/why-ai-generated-apps-rot) 5. [Prompt-to-app tools: what they do, where they stop](https://graphlit.co/blog/prompt-to-app-limits) 6. [Architecture drift: what it is and how to catch it](https://graphlit.co/blog/architecture-drift) 7. [Cursor, Copilot, Claude Code: assistants compared](https://graphlit.co/blog/ai-coding-assistants-compared) 8. [How to build an app without code: an honest guide](https://graphlit.co/blog/build-an-app-without-code) 9. [Using AI on an existing codebase without breaking it](https://graphlit.co/blog/ai-on-existing-codebase) 10. [The thousand-line prompt problem](https://graphlit.co/blog/thousand-line-prompt) 11. [How to review a codebase you didn't write](https://graphlit.co/blog/review-ai-generated-code) 12. [No-code vs AI builders vs hiring a developer](https://graphlit.co/blog/no-code-vs-ai-builders) 13. [Vibe coding, and the bill that arrives later](https://graphlit.co/blog/vibe-coding-bill) 14. [Architecture diagrams that stay true](https://graphlit.co/blog/diagrams-that-stay-true) 15. [Staying in control of software you can't read](https://graphlit.co/blog/non-technical-founder-control) 16. [Technical debt from AI coding: how to measure it](https://graphlit.co/blog/ai-technical-debt) 17. [From whiteboard sketch to working software](https://graphlit.co/blog/sketch-to-software) --- # Why AI keeps breaking features that already worked > The feature worked on Friday. On Monday a change to something unrelated broke it, and nothing in the toolchain said so. Here is why that keeps happening, and what actually helps. **Source:** https://graphlit.co/blog/why-ai-breaks-working-features **Published:** 2026-08-20 AI coding tools break working features because every change is made from a fresh reading of the codebase, by something with no memory of why the code is the way it is and no map of what depends on what. The model edits the file in front of it; the failure appears two files away, in a feature nobody asked it to touch. The compiler is happy, the diff looks plausible, and the break surfaces days later when a user hits it. That is the whole mechanism, stated up front. The rest of this article is the detail: why it is structural rather than a bug in any one tool, why the obvious fixes do not fix it, and what actually contains it. ## The shape of the failure Notice what kind of failure this is. It is not the first generation being wrong. First generations are where these tools shine. It is **change number twelve breaking change number three**: a regression, in the classic sense, except produced at a pace no human team could match. The sequence is always some version of the same story. You ask for a change to the checkout. The agent, reading the code fresh, notices the checkout shares a helper with the signup flow. It "improves" the helper: renames a parameter, tightens a type, removes a branch that looked dead. Checkout works. Signup, which nobody tested because nobody touched it, is broken. Except somebody did touch it; they just could not see that they were touching it. ## Three causes, all structural ### 1 · The model re-reads. It does not remember. Every session starts from zero. The reasons behind the existing code live in conversations and heads, not in the files: why the branch that looks dead is load-bearing, why the duplication is deliberate. So the model reconstructs intent by inference, every time, and inference is sometimes wrong. This is the same mechanism behind [why AI-generated apps rot](https://graphlit.co/blog/why-ai-generated-apps-rot); regressions are what it looks like change by change. ### 2 · Local edits have global consequences Code is shared. One helper serves four features; one table feeds nine queries. An edit is made *here*, but its consequences land wherever the shared thing is used, and nothing shows the model, or you, that blast radius at the moment of the edit. The change is local. The damage is topological. ### 3 · Nothing bounds the change Ask an agent to fix a button and it may reorganise imports in six files, because nothing tells it where the task ends. Instructions like "do not touch anything else" are advisory: [a wish, not a boundary](https://graphlit.co/blog/thousand-line-prompt). The model weighs them against its training-shaped instinct to tidy, and the instinct often wins. Without a mechanical boundary, the scope of any change is whatever the model decides it is. > **Better models shrink the frequency, not the category** > > A stronger model guesses intent better and breaks things less often. But it is still guessing. The information it would need is not in the codebase, because nobody wrote it down anywhere a machine can read. That is why this failure survives every model upgrade. ## What does not fix it - **More tests.** Tests pin the behaviour someone thought to pin. Regressions land precisely in the gap between what was tested and what was assumed, and a test suite says nothing about *structure*, so a helper quietly rewired underneath two features passes every test that does not exercise their intersection. - **Bigger context windows.** Fitting the whole codebase into context means the model can *read* everything, not that it *knows* anything. Reading is not memory, and it is certainly not a record of intent. The guess gets better-informed; it stays a guess. - **Sterner prompts.** "Be careful" and "change only this file" are inputs to a probability distribution. They shift it. They do not constrain it. ## What actually contains it Everything that works shares one property: it replaces an assumption with a check. These four are in rough order of effort, and the first two need no new tools at all. 1. **One intent per change, and read the diff, not the result**: Small changes make the blast radius reviewable. Before accepting anything, look at `git diff --stat`: the list of files touched is the first honest account of what actually happened. If you asked for a button fix and see six files, stop there. The interesting question is why, not whether the button works now. 2. **Pin behaviour before changing anything near it**: Before a change, add a test that captures what the *neighbouring* features currently do, the ones sharing files with your target. Characterisation tests are dull to write and they convert "nobody touched signup" from an assumption into something that fails loudly when it stops being true. 3. **Make ownership explicit**: Decide which files belong to which feature and write it down somewhere mechanical. Even a code-ownership file is a start. A change that strays outside its feature's files should be rejected by machinery, not caught by a tired reviewer. This is the single highest-leverage structural fix. 4. **Compare structure after every change, not just behaviour**: Tests ask "does it still behave?". The question that catches regressions early is "does it still have the shape we agreed?". Did an import cross a boundary, did a route stop existing, did a helper change hands. That requires a stated structure to compare against, which is what [architecture drift detection](https://graphlit.co/blog/architecture-drift) is. ## How Graphlit enforces the boundary Graphlit makes step three and step four mechanical. Because the system is a typed graph and every node knows its files, a build task arrives with an explicit scope: the nodes it serves, and therefore an allowlist of files it may touch. A change that strays outside the allowlist is stopped. Not reviewed, stopped. After each task the checks run and the code is re-read and hash-compared back to the graph, so the task either lands green or is reverted. *[Interactive demo: One task, end to end: scoped to its files, checked after, green or reverted.]* The honest limit, stated on [how it works](https://graphlit.co/how-it-works) and worth repeating here: a change can still be wrong *inside* its scope, in ways a typecheck cannot see. What the boundary buys is that the damage is contained to the feature you asked about, visible in the diff, and reversible as one unit, which is exactly the property the Monday-morning regression lacked. If you want to see what an explicit scope looks like against your own project, [import a repository](https://graphlit.co/get-started). The graph, and the file ownership it implies, is the first thing you get. ## Frequently asked ### Why does AI keep breaking my existing code? Because each change is made from a fresh reading of the codebase by a model with no memory of previous decisions and no map of what depends on what. It edits the file in front of it; the consequences land in features that share code with that file. The failure is structural. Better models make the guess better, but nothing in the codebase records the intent they would need to stop guessing. ### How do I stop an AI agent from touching unrelated files? Instructions help less than people hope. They are advisory, and the model weighs them against its instinct to tidy whatever it reads. The reliable fix is mechanical: give each task an explicit list of files it may touch and reject any change that strays, and check the diff's file list before accepting anything. A boundary that is enforced by machinery does not depend on the model's cooperation. ### What is a regression in software development? A regression is a change that breaks something which previously worked, usually a feature nobody meant to touch. The change itself often looks correct and passes review; the damage surfaces elsewhere, through shared code, and later, when someone finally exercises the broken path. AI-assisted development produces the classic regression pattern at much higher speed. ### Will bigger context windows stop AI regressions? No. A bigger context window lets the model read more of the codebase per change, which improves its inference. But reading is not remembering, and the decisive information is not in the code at all. Why a branch exists, which duplication is deliberate, where a boundary must hold: none of that is written anywhere a model can read, however large the window. --- # Sketch-to-code tools compared: tldraw, Uizard, and more > Several tools can turn a drawing into running code today. The real difference between them is what the drawing becomes afterwards: a discarded prompt, or a model of your system. **Source:** https://graphlit.co/blog/sketch-to-code-tools-compared **Published:** 2026-08-13 · **Updated:** 2026-08-13 Yes. AI can turn a sketch into working code, and it works today. A multimodal model can read boxes, arrows and handwriting from a drawing and produce something that runs. That part stopped being the interesting question about two years ago. The interesting question is what each tool does with the sketch *after* the code exists, because that is where they genuinely differ, and it decides whether the tool is a party trick or part of how you build. As with [our comparison of the AI app builders](https://graphlit.co/blog/ai-app-builders-compared), this is a comparison by architectural bet rather than by feature list. Features change monthly; the bet each tool has made does not. ## Three inputs that get called "sketch" The phrase covers three quite different starting points, and most disappointment with these tools comes from picking one built for a different input than yours. - **A freehand sketch**: boxes, arrows, handwriting. The tool has to read *intent*, meaning what you meant rather than what you drew. - **A screenshot or wireframe**: an existing interface, or a picture of one. The tool has to read *appearance*, and reproduce what is there pixel by pixel. - **A design file**: a Figma document with real layers and structure. The tool has to *translate*, because the structure already exists, it just is not code yet. ## The tools, by the bet each one made | Tool | The bet | Input | What you end up with | | --- | --- | --- | --- | | tldraw Make Real | The canvas is the prompt: draw, generate, annotate the result, generate again | Freehand shapes on its canvas | A running web prototype, rendered next to your drawing | | Uizard | The valuable artefact is an editable design, not code | Sketch, screenshot or text prompt | A design you refine and hand over. Expect wireframe fidelity, not a finished product | | screenshot-to-code | Open source, self-hosted, bring your own model keys | A screenshot | HTML, Tailwind, React or Vue markup reproducing the screenshot | | Visual Copilot (Builder.io) | The design file is the source of truth; translate it faithfully | A Figma selection | Framework code (React, Vue and others) meant to land in an existing codebase | *Verified against vendor documentation on 13 August 2026. These products move quickly, so check the current docs before deciding on one.* ### tldraw Make Real: the fastest idea-to-prototype loop there is You draw an interface on the canvas, select it, and a multimodal model returns working web code rendered right next to your drawing. The part that makes it more than a demo is the loop: the result is itself an object on the canvas, so you can draw an arrow at it, write "this button should be red", and run it again. For exploring an interaction idea in front of another person, nothing here is faster. It is honestly framed as an experiment on top of an open-source canvas, and the output is a prototype: a thing to learn from, not a codebase to keep. ### Uizard: when the deliverable is a design Uizard, now part of Miro, reads a sketch or a screenshot and gives you back an *editable design* on its canvas. That is a different bet from every other tool here: it assumes the thing you want next is not code but a design you can iterate, share and hand to whoever builds. If that is your situation (a founder who needs screens to react to, not a repository), the honest answer is that a design tool serves you better than a code generator. ### screenshot-to-code: the open-source workhorse An open-source project you run yourself, with your own model API keys. Drop in a screenshot and it produces clean markup (HTML with Tailwind, React or Vue) that reproduces it. Because you host it and pick the model, it is the natural choice when the screenshots are of something private, or when you want to compare what different models produce from the same image. What it makes is markup, faithfully: the appearance of the thing, not the system behind it. ### Visual Copilot: for teams that already live in Figma Builder.io's plugin converts a Figma selection into production-shaped code for the mainstream frameworks, aiming at your design system rather than generic output. It is solving the translation problem, and it is the right category when the structure already exists in a design file and the gap is purely design-to-code. It assumes a real design and a real codebase on either side of it, which is exactly why it is the wrong tool for a napkin sketch. One more option deserves a mention: the full-stack app builders. v0, Bolt and Lovable all accept an image among their inputs, so a sketch can seed an entire generated application. If that is the path you are weighing, [the app-builder comparison](https://graphlit.co/blog/ai-app-builders-compared) covers what happens after the first generation. ## The question that separates all of them Ask one thing of any tool in this category: **what happens to the sketch after the code exists?** In almost every case the answer is: nothing. The drawing was a prompt. It produced code, the code is now the artefact, and the drawing is scaffolding to be thrown away. That is fine for a prototype. But notice what is being discarded. The sketch was the one description of the system that said what you *meant*: which parts exist, what talks to what, where the boundaries are. > **The sketch is discarded at the exact moment it becomes true** > > The day the code is generated is the one day the drawing and the code agree. Throw the drawing away and every change after that widens a gap nobody can see: the same [architecture drift](https://graphlit.co/blog/architecture-drift) that eats hand-written systems, at generation speed. ## Where Graphlit fits Graphlit is in this category by input and a different one by output. You draw boxes and arrows on a canvas, or on paper you photograph, and a photograph is read by deterministic offline code before any model is asked. But the drawing does not become a one-shot prompt. It becomes a **typed graph** (services, routes, tables, jobs, and the edges between them) and the code is built against that graph, then re-read and compared back to it after every change. The sketch is not discarded; it is promoted to the thing the code has to keep agreeing with. *[Interactive demo: A drawing becoming a typed graph: the step where a picture turns into something checkable.]* So the honest positioning against everything above: if you want a prototype this afternoon, use Make Real. If you want a design, use a design tool. If you want the drawing to still mean something on day ninety, that is the problem Graphlit exists for. [How it works](https://graphlit.co/how-it-works) walks through the whole loop, and it is [free to start](https://graphlit.co/get-started). ## How to choose 1. **You want to try an interaction idea today**: tldraw Make Real. The draw-generate-annotate loop is the best rapid-prototyping interface in the category, and treating the output as disposable is the intended use. 2. **You have a screenshot and want matching markup**: screenshot-to-code if you are comfortable self-hosting with your own model keys; it reproduces appearance faithfully and keeps private screenshots on your infrastructure. 3. **You need screens people can react to, not a repository**: Uizard. The editable design is the deliverable, and pretending otherwise leads to disappointment in both directions. 4. **You have a Figma file and an existing codebase**: Visual Copilot. The translation problem is the one it is built for, and the output is meant to land in a real repository. 5. **You want the sketch to become the system's living map**: That is the architecture-graph bet: the drawing stays connected to the code and something checks the two still agree. It is the bet Graphlit makes, and [the features page](https://graphlit.co/features) shows what falls out of it. > **A note on this comparison** > > We build Graphlit, so weigh the last sections accordingly. The rest of the article is written to be useful even if you never touch it. Matching the tool to your input, and knowing what happens to the sketch afterwards, are the two decisions that matter regardless of whose product you pick. ## Frequently asked ### Can AI turn a sketch into working code? Yes. Several tools do it today, and it works. A multimodal model can read boxes, arrows and labels from a drawing and produce running code from them. The differences between tools are what the sketch becomes afterwards: in most, the drawing is a one-shot prompt that is discarded once code exists; in a few, it becomes a persistent model of the system that the code is checked against as it keeps changing. ### What is the difference between sketch-to-code and screenshot-to-code? The input and the job. A sketch expresses intent: rough boxes and labels a model must interpret to decide what you meant. A screenshot is an existing interface a model must reproduce as faithfully as possible. Tools built for one are usually mediocre at the other, so name which input you actually have before choosing. ### Do sketch-to-code tools produce production-quality code? Treat the output as a prototype or as scaffolding, not as a finished product. What these tools produce runs, and that is genuinely useful for exploring an idea. But production quality is about error handling, security and fit with the rest of your system, none of which is visible in a drawing. Review anything you intend to keep. ### Can I turn a whiteboard photo into an app? Yes. Some tools accept a photo of paper or a whiteboard directly and read the boxes, arrows and handwriting out of it. One caution: a whiteboard photo is often somebody's unreleased architecture, so it is worth checking whether the tool uploads the image to a third-party service or reads it locally before you feed it anything sensitive. --- # AI app builders compared: v0, Bolt, Lovable, Replit > Four tools, four different bets on what 'building an app' means. Here is what each is actually good at, and the one problem none of them solve. **Source:** https://graphlit.co/blog/ai-app-builders-compared **Published:** 2026-08-09 · **Updated:** 2026-08-09 Every one of these tools can take a sentence and give you a running application. That was science fiction three years ago and it is a commodity now. Which means the interesting question is no longer *can it build this*. It is **what happens on day thirty**. This is a comparison by architecture rather than by feature list. Feature lists go stale in a quarter; the shape of the bet each tool has made does not. ## Four different bets They get grouped together as "AI app builders", but they are solving noticeably different problems. | Tool | The bet | Strongest when | You own | | --- | --- | --- | --- | | v0 | Full-stack generation, tightly coupled to a deployment platform | You want production-grade React screens and a one-click path to live | A synced repository | | Bolt | A full stack running in the browser, no local setup | You want to go from idea to something clickable with zero environment work | A project you can export | | Lovable | Full-stack app generation with a managed backend and repo sync | You want a working product with auth and a database without wiring them | A synced repository | | Replit | A cloud IDE where an agent builds, runs and hosts | You want building, running and shipping in one place | A hosted workspace | *Verified against vendor documentation on 9 August 2026. These products move quickly, so check the current docs before making a decision on one.* ## What each one is genuinely good at Skipping the marketing, here is the honest case for each. All four are good tools, and the criticism later in this article applies to the category, not to any one team's execution. ### v0: the fastest path to a good-looking interface Interface quality is where v0 is hardest to beat. Component-level generation with a tight iteration loop means you converge on a design in minutes, and the output is ordinary React you can read rather than a proprietary format. It started as a UI tool and is no longer only that. It now describes itself as building full-stack apps, plans its own work, connects to databases and deploys in a click. Worth knowing if you last looked a year ago and filed it under "screens". The interface is still the part that stands out. ### Bolt: the shortest distance from nothing to running Running the whole toolchain in the browser removes an entire category of friction. No install, no version mismatch, no "works on my machine" before you have even started. For prototyping and for teaching, that is a genuine advance. The constraint is that a browser sandbox is not where a serious production system eventually lives, so at some point there is a move. ### Lovable: the most complete first version Generating an app *with* a backend (auth, database, the connections between them) is a much harder problem than generating screens, and getting a coherent first version out of a prompt is real value. Repository sync matters more than it sounds: it means the output is code you keep rather than a platform you rent. ### Replit: one place for the whole loop Build, run, debug and host without leaving the tab. For a solo builder, collapsing four tools into one is worth a lot, and having the agent work in the same environment the app runs in removes a class of integration problems. ## The failure mode they share Here is the pattern, and it is remarkably consistent across all of them. The first prompt is magic. The second is fine. Somewhere around the fifth or sixth change (usually the one that touches something built two weeks earlier), the model edits the wrong file, or writes a second function that does what an existing one already did, or quietly breaks a screen nobody opened during testing. Nothing is *broken*, exactly. It compiles. It deploys. But you have stopped being able to predict what a change will do, and that is the moment a codebase turns from an asset into a liability. > **The cause is structural, not a model weakness** > > These tools generate code from a **prompt**, and a prompt is not a durable description of a system. It has no memory of the decisions behind the existing code, so on the tenth change the model is re-reading your codebase and guessing at intent that was never written down. Better models make the guess better. They do not remove the guessing. There is a name for the gap that opens up between what you believe the system is and what the code actually does: [architecture drift](https://graphlit.co/blog/architecture-drift). It is the reason a generated app that worked beautifully in week one becomes frightening to change by week four. ## How to actually choose Match the tool to the lifespan of what you are making. That single question resolves most of the decision. 1. **Throwaway prototype, days to weeks**: Any of them. Optimise purely for speed to something clickable. You are going to delete it. Bolt and Replit have the least setup. 2. **A real product you intend to maintain**: Pick the one that gives you a repository you own, and get the code into version control on day one. Ownership of the output matters more than the quality of the first generation. 3. **Something that touches money, health or personal data**: Use these tools for the interface and the scaffolding, then have someone review the parts that carry risk. Generated auth and generated payment flows are exactly where a plausible-looking mistake is most expensive. 4. **An existing codebase**: None of these are for you. They are greenfield tools. See [using AI on an existing codebase](https://graphlit.co/blog/ai-on-existing-codebase). ## Where Graphlit fits Graphlit is not a fifth prompt-to-app tool, and if a prompt-to-app tool is what you need you should use one of the four above. It starts from a different premise: the durable artefact is not the prompt and not the code, but **the architecture**. The boxes, the arrows, and the rules about which box is allowed to talk to which. You draw that. It becomes a typed graph. The code gets built against it, and then the code is re-read and compared back to the drawing after every change. That last step is the part that matters. *[Interactive demo: Drift detection, running live. Change the code and the drawing stops agreeing with it.]* So the honest positioning is: those tools compete on the ceiling, on how impressive the first generation is. This one is about the floor, about what is still true on day ninety. If your project never sees day ninety, the floor does not matter. > **A note on this comparison** > > We build Graphlit, so treat the last section as what it is. The rest of the article is written to be useful even if you never use it. The failure mode described above is real regardless of what you build with, and knowing it exists is most of the defence. ## Frequently asked ### Which AI app builder is best for beginners? Replit or Bolt, because neither requires you to set up a development environment. Bolt runs entirely in the browser; Replit gives you a cloud workspace where the app also runs and deploys. Both remove the setup step that stops most beginners before they write anything. ### Can I use AI app builders for production applications? Yes, with two conditions: make sure you own the output as a repository under version control, and have anything security-sensitive reviewed by someone who can read it. Generated authentication, permissions and payment code is where a plausible-looking mistake costs the most. ### Why do AI-generated apps get harder to change over time? Because a prompt is not a durable record of the system. Each new change makes the model re-read the codebase and infer intent that was never written down, so small misreadings accumulate. The code keeps compiling while it stops matching your mental model of it. ### Do I still need to know how to code? To ship something small, no. To keep it running as it grows, you need either the ability to read the code or a way to verify it against something you can read. That gap is what architecture-level tools are trying to fill. --- # Why AI-generated apps fall apart in week three > It is never the first prompt that goes wrong. The decay has a mechanism, and once you can name it you can design around it. **Source:** https://graphlit.co/blog/why-ai-generated-apps-rot **Published:** 2026-08-09 The pattern is consistent enough to set your watch by. Week one is euphoric. Week two is productive. Somewhere in week three you make a small change and something unrelated stops working, and when you go looking for why, you find code you do not recognise doing a job you thought was handled somewhere else. This is not bad luck and it is not a bad model. It is a mechanism, and it works the same way every time. ## The mechanism Every AI change to a codebase follows the same three steps: **read the code, infer the intent, write the change.** Step two is the whole problem. Intent is not in the code. The code records *what* was decided, never *why*. When a model reads a repository it sees the what, reconstructs a plausible why, and edits on the basis of that reconstruction. | What the code shows | What was actually meant | What the model may do | | --- | --- | --- | | Two functions that validate an email | One is for public signup and deliberately stricter | Merge them, quietly loosening public signup | | A retry loop around one API call | That upstream service is known to be flaky | Remove it as redundant complexity | | A table with duplicated columns | Denormalised on purpose, for read performance | Normalise it, and slow the main query down | | Frontend calls the API, never the database | A deliberate boundary: auditing lives in the API | Add a direct database call, because it is shorter | *Every row compiles. Every row passes the existing tests.* Note what these have in common. None of them is a crash. Each is a *reasonable* change given only the code, and wrong given the reasoning nobody wrote down. ## The four symptoms, in the order they appear 1. **Duplication**: The same job done in two places, because the model did not find the first one. Cheap on its own. Expensive because now a fix has to be applied twice and nobody knows that. 2. **Boundary erosion**: A layer that was supposed to be crossed one way starts being crossed several ways. Usually invisible until something that depended on the boundary (an audit log, a permission check, a cache) turns out to have a hole. 3. **Orphaned code**: Functions and files nothing calls any more, left behind by changes that routed around them. They make every future search noisier and every future change slower. 4. **Fear**: The terminal state. Nobody is confident what a change will do, so changes get bigger and rarer and riskier, and the project stops moving. > **Why tests do not catch this** > > Tests check behaviour someone thought to write a test for. Every symptom above preserves behaviour, which is precisely why they are hard to spot. A duplicated validator returns the right answer. An eroded boundary still serves the page. The system is wrong structurally while behaving correctly, and no assertion is watching structure. ## Why it accelerates Decay is not linear, for a reason worth understanding: **each generation reads the previous one's output as if it were intentional.** Change four introduces a duplicate validator. Change nine reads a codebase where validation happening in two places is now the observed convention, so it adds a third. Change fifteen sees a codebase with no consistent validation strategy and picks arbitrarily. > Yesterday's accident is today's precedent. The model is not degrading. It is faithfully following a convention that was never a decision. ## What does not fix it - **A better model.** It reads the same absent information more thoroughly. The missing why is still missing. - **A longer prompt.** It works for one change. The next session starts from the code again. See [the thousand-line prompt problem](https://graphlit.co/blog/thousand-line-prompt). - **More tests.** Genuinely valuable, and aimed at a different target. Tests pin behaviour; this is structure. - **A diagram in a document.** Correct on the day it is drawn. Nothing connects it to the code, so it silently stops being true and is then worse than nothing, because people trust it. ## What does The intent has to exist somewhere outside the code, in a form that can be **mechanically compared against** the code. Those two properties together, not either alone. 1. **A structural description of the system**: the parts, and which parts may talk to which. 2. **Rules that are checkable**, not prose: "the frontend must not call payments directly" as something that fails a build. 3. **A comparison that runs on every change**, so divergence is reported the day it happens rather than discovered in week twelve. That is what Graphlit is. The drawing is the intent. It is typed, so it can be checked. The code is re-read after every change and compared back to it, and anything that no longer matches is reported. *[Interactive demo: Change the code, and the map stops agreeing. Drift becomes a report, not a surprise.]* > **The honest limit** > > This catches structural divergence: code that no longer matches the design. It cannot tell you the design was right in the first place, and a feature can still be wrong while matching its drawing perfectly. Verification narrows the space of possible mistakes. It does not empty it. ## Frequently asked ### Why does AI-generated code get worse over time? Because each change reads the previous output as if it were deliberate. An accidental pattern introduced early becomes the observed convention, and later changes reproduce it. The model is not degrading. It is faithfully copying something that was never a decision. ### Can more tests prevent AI code from decaying? They help, but they target the wrong layer. The common failure modes (duplicated logic, crossed architectural boundaries, orphaned code) all preserve behaviour, so behavioural tests pass. What is needed is a structural check about which parts may call which. ### How do I know if my codebase is already drifting? Three practical signals: you find two pieces of code doing the same job, you cannot confidently say which parts call which, or you avoid changing certain files. Any one of them means the map in your head has stopped matching the code. ### Is this a problem with a specific AI tool? No. It follows from generating code out of prompts against a codebase whose reasoning was never recorded, so it appears across every tool in the category. Tools differ in how quickly you reach it, not in whether you do. --- # Prompt-to-app tools: what they do, where they stop > The first version is nearly free now. Understanding exactly where that stops being true is the difference between a tool that saves you months and one that costs you them. **Source:** https://graphlit.co/blog/prompt-to-app-limits **Published:** 2026-08-09 Describe an app. Get an app. It works, and it is genuinely one of the more remarkable things software has learned to do. It also has a boundary, and the boundary is sharper than the marketing suggests. This article is about where it sits and why it sits there, because if you know it in advance you can design around it, and if you do not you will discover it around week three with a codebase you no longer understand. ## What they are reliably excellent at Not damning with faint praise: these are real, large wins. - **The blank page.** Going from nothing to a running skeleton used to take a day of setup and boilerplate. It now takes a sentence, and that removes the single biggest barrier to starting. - **Conventional shapes.** A CRUD screen, a settings page, a signup flow, a table with filters. Patterns that appear in thousands of codebases are exactly what a model has seen thousands of times. - **Breadth you do not have.** Scaffolding a piece of the stack you have never touched, competently enough to get moving, is worth an enormous amount to a small team. - **Throwaway work.** Prototypes, spikes, one-off internal tools, demos for a meeting on Thursday. Anything with a short lifespan is pure upside: the long-term costs never arrive. > **If your project is genuinely short-lived, stop reading** > > Everything below is about maintenance cost. A prototype you will delete in a fortnight never pays it. Use the fastest tool you can find and do not over-think it. ## Where it stops The boundary is not a difficulty ceiling. It is not that these tools can do easy things and not hard ones. They will attempt something quite hard and often succeed. The boundary is **accumulated context**. It appears at the point where the correct next change depends on decisions made earlier that were never written down. ### Change one: perfect Empty repository, clear instruction, no history to be consistent with. The model has complete information. ### Change eight: a coin flip Now there are forty files. Some of them contain deliberate decisions: this table is denormalised on purpose, that retry exists because the upstream service is flaky, this validation lives in two places because one path is public. None of that reasoning is in the code. The model reads the files, infers a plausible intent, and acts on the inference. Sometimes the inference is right. When it is wrong, what you get is not a crash. It is a **second implementation of something that already existed**, or a call that skips a boundary it was supposed to go through. Silent, plausible, and compiling. | | First generation | Tenth change | | --- | --- | --- | | Information available | Your whole intent, in the prompt | Whatever survived into the code | | Failure mode | Obviously wrong: you see it | Plausibly wrong: you don't | | Cost of a mistake | Regenerate, seconds | Debug unfamiliar code, hours | | What you can verify | Does it run? | Does it still mean what I meant? | ## Why a bigger model does not fix it This is the part worth sitting with, because the intuitive response is to wait for the next model release. The missing information is not in the codebase. A larger context window lets a model read more of what exists; it does not let it read a decision that was made in a conversation in March and never recorded. You cannot retrieve what was never written. > Every prompt-to-app tool is doing archaeology on its own output. Better models make better archaeologists. They do not turn archaeology into documentation. ## Working past the boundary Three practical moves, in rough order of effort. 1. **Write intent down where the tools can see it**: A short, factual document in the repository describing what the parts are and the rules between them. Not a wiki page: something in version control, next to the code, that an agent will actually read. 2. **Make the important rules mechanical**: "The frontend must not call payments directly" is a sentence a human can ignore. As a check that fails a build, it is a rule. Anything you genuinely care about should be the second kind. 3. **Keep a map that cannot go stale**: A diagram maintained by hand is a diagram that is wrong. A map generated from the code and re-checked against it stays honest, which is the only way it stays useful. Graphlit is a bet on the third one. You draw the architecture; it becomes a typed graph; the code is built against it and then continuously compared back to it. The drawing is not documentation that drifts. It is checked, and when the code stops matching it, that is a report rather than a surprise. *[Interactive demo: The full loop: draw, type, plan, build, verify, and check the drawing again.]* > **What this does not fix** > > A generated feature can still be wrong in ways no automated check catches: a rule enforced perfectly is still only as good as the rule. Verification tells you the code matches the design. It does not tell you the design was right. ## Frequently asked ### Are prompt-to-app tools good enough for a real business? For the first version, often yes. The question is not whether the generated code works but whether you can keep changing it safely six months later. Teams that succeed with these tools tend to get the output into version control early and add architectural checks before the codebase gets large. ### At what point do prompt-to-app tools start struggling? Around the point where a correct change depends on a decision made earlier that was never written down. In practice that is usually a few dozen files in, or the first time you modify something built more than a couple of weeks ago. ### Will better AI models solve this? Only partly. Larger models read existing code better, but the information they are missing (why the code is the way it is) was never recorded anywhere. That is a documentation problem, not a model problem. --- # Architecture drift: what it is and how to catch it > Every codebase has a design in someone's head and a design in the files. Drift is the distance between them, and it only ever grows on its own. **Source:** https://graphlit.co/blog/architecture-drift **Published:** 2026-08-09 Ask two engineers on the same team to draw the system on a whiteboard. You will get two different pictures, and neither will match the code. That gap has a name: **architecture drift**. It is one of the few problems in software that gets worse when nobody does anything at all. ## A precise definition Drift is the divergence between the **intended** architecture and the **implemented** one. It is worth being strict about the word. Not every change is drift. Deliberately deciding the frontend may now query the read replica directly, writing that down, and updating the design is architectural *evolution*, healthy and normal. Drift is when the code changes and the intent does not follow, so the two silently disagree. | | Evolution | Drift | | --- | --- | --- | | The design changed | Yes, deliberately | No, only the code did | | Someone decided | Yes | It happened as a side effect | | Written down | Yes | No | | Discovered | When it happens | During an incident | ## The four sources ### 1 · Expedience A deadline, a direct call that skips a layer, a note to clean it up later. The note is never actioned. This is the classic source and it long predates AI. ### 2 · Ignorance Someone new does not know the rule, because the rule lives in a conversation they were not in. They write something reasonable that violates a constraint nobody told them about, and it passes review because the reviewer did not spot it either. ### 3 · Generated code The modern accelerant. An AI agent is permanently in the position of the new starter: every session, no memory of the reasoning, inferring the rules from the files. It is fast, so it drifts fast. See [why AI-generated apps fall apart](https://graphlit.co/blog/why-ai-generated-apps-rot). ### 4 · Deletion The quiet one. A service is decommissioned, a table is dropped, a module is folded into another. The code is correct; the diagram now describes a system that no longer exists. Drift by subtraction still misleads everyone who reads the diagram. ## Why it stays invisible Drift has no symptoms until it has expensive ones. Nothing in the ordinary toolchain is looking for it: - **The compiler** checks types, not topology. A frontend importing the payments module directly is perfectly well-typed. - **Tests** check behaviour. Every drift example preserves behaviour. That is what makes it drift and not a bug. - **Linters** check style and local patterns, not which module may depend on which. - **Code review** checks the diff. Drift is a property of the *whole system*, and it is nearly impossible to see a hundred-line diff and notice it has quietly created the fourth path into the database. > **The bill arrives as an incident** > > Drift is not billed as "we have drift". It is billed as an outage nobody can explain quickly, an audit finding, a data leak through a path nobody knew existed, or a two-week estimate for a change that should have taken a day. By then the cost is already sunk. ## How to detect it All the working approaches share one shape: **a machine-readable description of the intended architecture, plus something that compares it to reality automatically.** The manual alternative (periodic architecture reviews) finds drift months after it happened, which is better than nothing and much worse than a check. 1. **Dependency rules**: Declare which modules may import which, and fail the build on a violation. The cheapest useful step, and there are mature libraries for most language ecosystems. Catches boundary erosion specifically. 2. **A generated map**: Derive the architecture diagram from the code rather than drawing it. It cannot go stale, because it is regenerated. It tells you what *is*, though not what was *intended*. 3. **Intent, compared to code**: Keep a description of the intended system, and diff it against the generated map on every change. This is the one that catches all four sources, because it has both halves: a stated intent, and an observed reality to check it against. Graphlit does the third. The drawing is the intent: typed, versioned, and stored with the code. Every file it builds is hash-mapped back to the node that owns it, so after any change the code can be re-read and compared: what moved, what vanished, what was never built, and what appeared that nothing accounts for. *[Interactive demo: A drift report: each node hash-checked against the file on disk.]* ## Making drift a decision The goal is not zero drift. Zero drift means nothing is changing. The goal is that every divergence becomes a **decision** rather than an accident. When the check reports that the frontend now calls the database directly, exactly one of two things is true, and either is fine as long as somebody chooses: 1. That was a mistake. Revert it. 2. That was deliberate. Update the design, and now the drawing is true again. What is not fine is the third case, which is what happens by default: nobody knows it happened, and the diagram on the wall quietly becomes fiction. ## Frequently asked ### What is architecture drift? The divergence between a system's intended architecture and its implemented one. It happens whenever code changes without the design being updated to match, and unlike most software problems it grows without anyone doing anything. ### What is the difference between architecture drift and technical debt? Technical debt is usually a known shortcut you intend to repay. Drift is unknown: the gap between belief and reality. Debt is a decision with a cost; drift is a decision nobody made, which is why it is harder to plan around. ### How do you measure architecture drift? You need a machine-readable statement of the intended architecture and a map derived from the actual code, then you compare them. Useful measures are the count of dependency-rule violations, components in the code with no counterpart in the design, and components in the design with nothing implementing them. ### Can architecture drift be prevented entirely? No, and preventing it is the wrong goal, because some divergence is legitimate evolution. The achievable goal is to make every divergence visible quickly, so it becomes a deliberate decision to accept or revert rather than something discovered during an incident. --- # Cursor, Copilot, Claude Code: assistants compared > These tools write code well. The differences that matter are about scope: how much of your system each one can hold in its head at once. **Source:** https://graphlit.co/blog/ai-coding-assistants-compared **Published:** 2026-08-09 · **Updated:** 2026-08-09 An AI coding assistant is a different product from an [AI app builder](https://graphlit.co/blog/ai-app-builders-compared). App builders make something from nothing. Assistants work inside a codebase that already exists, alongside someone who can read it. The category is mature enough that raw code quality is no longer the differentiator. They are all good at writing a function. What separates them is **how much of your system each one can see and act on at once**. ## The scope ladder Every assistant sits somewhere on a ladder from "completes the line you are typing" to "executes a multi-file task on its own". Higher on the ladder means more leverage and more blast radius. | Tool | Works at the level of | Best for | What you supervise | | --- | --- | --- | --- | | GitHub Copilot | The line, the file, and (in agent mode) a whole task | Broad rollout: it is the low-friction, compliance-friendly default | A suggestion, or a delegated task's result | | Cursor | The file and the project, with retrieval across it | Multi-file edits where you stay in the loop | A diff, before you accept it | | Devin Desktop *(formerly Windsurf)* | The project, with an agentic edit loop | Larger refactors that touch several files | A proposed change set | | Claude Code | The repository and the terminal: it can run things | Whole tasks: implement, run the tests, fix what failed | The outcome, and the commit | *Verified against vendor documentation on 9 August 2026. These products iterate constantly, so treat this as a map of approaches rather than a current feature matrix, and re-check before acting on it.* Moving up the ladder is a real trade, not a straight upgrade. Line-level completion is nearly impossible to get badly wrong and saves you minutes. Repository-level agents save you hours and can be wrong in ways that take a while to notice. ## Choosing by how you work 1. **You already know exactly what to write**: Copilot, in completion mode. The lowest overhead per keystroke, and you catch mistakes instantly because you were about to write the correct version anyway. It has an agent mode too, but that is a different row of this table, not this one. 2. **You are working across several files at once**: Cursor or Devin Desktop. Project-level retrieval is the difference between an assistant that suggests a plausible function and one that suggests the function your codebase already has a convention for. 3. **You want to hand over a whole task**: Claude Code, or any agent that can run your tests. The ability to execute is what turns "here is some code" into "here is a change that passes". 4. **You cannot read the output**: None of these, on their own. Every one of them assumes a reviewer. See [staying in control of software you cannot read](https://graphlit.co/blog/non-technical-founder-control). ## What none of them track Here is the thing they have in common, and it is not a criticism of any individual product. It is a property of the category. **An assistant's context is the code.** It reads your files, and it is extremely good at answering "what does this codebase do?" What it cannot read is the thing that was never written down: what the codebase was *supposed* to do. > The payments service must never be called directly from the frontend. It goes through the API gateway, because that is where the audit log lives. That rule exists in someone's head, and maybe in a diagram in a document that has not been opened since March. An assistant that reads only code will happily wire the frontend straight to payments, because nothing in the files says not to. The code compiles. The tests pass. The audit log has a hole in it. > **Passing tests is a weaker signal than it looks** > > Tests check the behaviour someone thought to write a test for. Architectural rules (what may call what, what must go through which boundary) are almost never encoded as tests, so an agent can violate every one of them and get a green run. ## Making the rules readable The practical fix is to stop keeping architecture in your head and start keeping it somewhere both you and the tools can read. - **Write the rules down as rules**, not prose. "Frontend must not import payments directly" is checkable. A paragraph in a wiki is not. - **Put them next to the code**, in the repository, so they are versioned with the thing they describe. - **Check them automatically**, on every change, so a violation is a failed check rather than something spotted in review three weeks later. - **Keep a map of the system** that is generated from the code, so it cannot quietly stop being true. That last point is what Graphlit does. It reads an existing repository, produces a typed graph of the services, routes, tables and the calls between them, and lets you declare rules on top of it, which are then enforced on every change. *[Interactive demo: Importing an existing repository into a typed architecture graph.]* It is not a replacement for the assistants above; it runs alongside them. They write the code. The graph is what says whether the code they wrote still fits the system you meant to build. ## Frequently asked ### Is Cursor better than GitHub Copilot? They overlap more than they used to. Copilot now has an agent mode that edits across files, so the old line between them has blurred. Copilot is still the lowest-friction option for a large team and the safest for compliance; Cursor is generally preferred by developers working in bursts of whole-feature changes. Both are good; the deciding factor is usually the team, not the tool. ### Can AI coding assistants work on large existing codebases? Yes, and that is where they earn the most, but the limitation is context. They can read the code and cannot read undocumented intent: the architectural rules that live in people's heads. On a large codebase that gap is where the expensive mistakes come from. ### Do AI assistants introduce security vulnerabilities? They can, in the same way any fast-moving contributor can. The higher risk is not exotic bugs but ordinary ones at scale: a missing authorisation check on one route out of forty. That is why review and automated checks matter more, not less, as the volume of generated code goes up. --- # How to build an app without code: an honest guide > This is now genuinely possible, and the guides that say so usually stop before the interesting part. Here is the whole arc, including the bits that get harder. **Source:** https://graphlit.co/blog/build-an-app-without-code **Published:** 2026-08-09 Building software without writing code has gone from marketing claim to ordinary fact. What has not changed is that **building it is the easy part**. Most of the difficulty in software has always been in changing it afterwards, and that is where guides tend to go quiet. This one does not. Assume you are non-technical and want something real. ## Step 1: Describe the system, not the app The instinct is to describe features: "users can sign up, browse listings, book a slot, pay." Useful, and not the thing that decides whether this goes well. Describe the **parts** instead, and what talks to what. You do not need technical vocabulary. Boxes and arrows on paper are exactly right: - A box for each thing that exists: a screen, a stored list of things, an outside service you rely on. - An arrow for each connection: this screen reads that list, this action charges that payment provider. - A note on anything that must never happen: "nobody sees another customer's bookings." That drawing is the most valuable thing you will produce, and it takes twenty minutes. It is what you will hand to a tool, to a developer, or to yourself in six months. *[Interactive demo: Boxes and arrows becoming a typed map. No code involved in producing the input.]* ## Step 2: Pick a route Covered in depth in [no-code vs AI builders vs hiring](https://graphlit.co/blog/no-code-vs-ai-builders); the short version: | If you | Use | Because | | --- | --- | --- | | Are testing whether anyone wants this | A no-code platform | Fastest to something real; you will probably delete it | | Want to own what you build | An AI app builder that exports a repository | You keep source code rather than a rented configuration | | Are handling money, health or sensitive data | A professional for those parts | This is where a plausible-looking mistake is most expensive | ## Step 3: Build the smallest honest version Not "a minimum viable product" in the sense of a worse version of everything. One complete path, done properly, end to end. If it is a booking product: one person signs up, sees real availability, books, and receives a confirmation. No admin panel, no settings, no dashboard. A single path that genuinely works teaches you more than five half-built ones, and it is small enough that you can still hold it in your head. > **Do this on day one, not later** > > Get the code into version control (GitHub or similar) from the very first version, even if you do not understand what it is. It costs an hour, it is the difference between owning software and renting it, and every future option (hiring someone, changing tools, recovering from a bad change) depends on it existing. ## Step 4: Where it gets harder This is the part the guides skip. Around change eight or ten, something you built earlier breaks while you are working on something else. The reason is not that you did something wrong. It is that the tool building your app cannot see *why* the earlier decisions were made. It re-reads the code each time and infers. The inference is usually right and occasionally not, and the wrong ones do not announce themselves. [The full mechanism is here](https://graphlit.co/blog/why-ai-generated-apps-rot). What helps, in order of value for effort: 1. **Change one thing at a time**: One change, check it works, save it. Bundling five changes means that when something breaks you have five suspects and no way to separate them. 2. **Keep your drawing current**: When you add a part, add the box. This takes seconds and it is the only record of what the system is supposed to be. 3. **Write down the rules that must not break**: "Customers only ever see their own bookings." Keep the list short and check it after every significant change. Three real rules beat twenty aspirational ones. 4. **Get the risky parts reviewed once**: A few hours of a developer's time on login, payments and permissions is the highest-value money you will spend. You are not asking them to rewrite anything, just to look. ## What you cannot avoid Being honest about this is more useful than pretending otherwise: - **Someone will eventually need to read the code.** Not you, necessarily, and not soon. But the first serious production problem is diagnosed by reading, and no tool has removed that. - **Security is not automatic.** Generated apps commonly get login right and then miss one permission check on one screen. It is not exotic and it is not visible from the outside. - **You are accountable for the data.** Legally, whoever collects personal data is responsible for it, regardless of what built the software. None of these is a reason not to build. They are reasons to keep the drawing, own the code, and buy a few hours of review at the right moment. ## Where Graphlit fits Graphlit is built on the premise that step 1 should be the durable artefact. You draw the system; that drawing becomes a typed map; the code is built against it and then continuously checked back against it, so when something no longer matches, you are told rather than finding out later. For a non-technical builder the practical value is that the thing you can read (the drawing) stays connected to the thing you cannot. You can [create an account](https://graphlit.co/get-started) and try it. ## Frequently asked ### Can I really build an app without knowing how to code? Yes, and it is now genuinely practical rather than a marketing claim. The realistic limit is not building it but maintaining it: around the tenth significant change, things built earlier start breaking in ways that are hard to diagnose without reading code. ### How much does it cost to build an app without a developer? Tool subscriptions are typically tens of dollars a month, so the upfront cost is small. The costs that matter arrive later: platform pricing at scale, or a few hours of professional review for security-sensitive parts. Budget for the review. It is the cheapest insurance available. ### What is the biggest mistake non-technical founders make? Not getting the code into version control early. Without it there is no history to recover from, no way to see what a change did, and no clean handover to a developer later. It costs an hour on day one and is very expensive to retrofit. ### When do I need to hire a real developer? Three triggers: you are handling payments, health data or anything regulated; you have a production incident you cannot diagnose; or you have stopped being able to predict what a change will do. The first is a planned hire, the other two are already late. --- # Using AI on an existing codebase without breaking it > Almost every AI coding demo starts with an empty folder. Almost no real work does. Here is how to bridge the gap without wrecking something that already works. **Source:** https://graphlit.co/blog/ai-on-existing-codebase **Published:** 2026-08-09 The demo is always an empty directory. Your situation is four years of code, three people who have left, and a system that is currently making money, which means the downside of a bad change is not a wasted afternoon. This is the harder problem and the more valuable one, and it needs a different method. ## Why greenfield technique fails here | | Empty folder | Existing codebase | | --- | --- | --- | | Context needed | Your prompt | Four years of undocumented decisions | | Cost of a wrong guess | Regenerate | Break something that works | | Conventions | Whatever the model likes | Ones you must match exactly | | Verification | Does it run? | Did anything *else* change? | The last row is the crux. In greenfield, working is the goal. In brownfield, working is the *starting position*, and the entire risk is in unintended change elsewhere. ## The method 1. **Get a map before you get an agent**: Know what the parts are and what calls what, before anything writes code. An agent let loose on a system nobody has mapped will produce changes nobody can evaluate. This is the step people skip and the one that decides the outcome. 2. **Scope every task to named files**: Not "add caching to the product page" but "add caching, touching only these four files". A scoped task can be reviewed by a person who did not write it; an unscoped one cannot. 3. **Make the boundaries mechanical**: Whatever rules keep the system coherent (which layer may call which) should fail a build rather than live in someone's memory. An agent will honour a check and cannot honour a convention it has not been told about. 4. **Verify structurally, not just behaviourally**: Tests tell you the behaviour you thought to test still works. Also ask: did this change touch files it had no business touching, and does the system's shape still match the map? 5. **One task per commit**: The single most valuable habit. When something breaks in a week, the difference between a five-minute bisect and a two-day investigation is whether commits are atomic. ## Scoping, concretely "Scope the task" sounds like advice until you have to do it. In practice it means answering three questions before the agent starts: 1. **Which files may this change touch?** Write the list. If you cannot, you do not understand the change well enough to supervise it. 2. **What must remain true afterwards?** The existing behaviour this must not disturb: the thing you will check. 3. **How will I know it went wrong?** A specific check, not "the app still loads". > **The file list is the safety mechanism** > > It converts review from "read the whole diff and hope" into a question with an objective answer: did it stay inside the lines? A change that touches a file outside its allowlist is worth stopping on principle, before anyone reads what it did. Graphlit enforces this directly: each task names the graph nodes it owns, which resolve to the files it is allowed to modify, and a run that edits outside that set is rejected rather than reviewed. *[Interactive demo: One task, fenced to an allowlist, verified before it is kept.]* ## Where AI is genuinely strong on old code It is easy to read the above as caution. There are places where AI is *better* on an existing codebase than on a new one: - **Explaining code nobody understands.** Handing a model a gnarly file and asking what it does is genuinely excellent, and it is read-only, so the risk is zero. - **Mechanical migrations.** Renaming an API across two hundred call sites, moving to a new library version, updating a deprecated pattern. Tedious, well-specified, and verifiable. - **Writing the missing tests.** Adding characterisation tests to legacy code is exactly the kind of work people avoid, and it makes every later change safer. - **Finding duplication.** "Where else does this pattern appear?" over a large codebase is a search problem models are good at. Notice the pattern: these are all tasks where **the correct answer is checkable**. That is the real dividing line, and it is a better guide than greenfield-versus-brownfield. ## The one thing to do first If you take one action: **import your codebase into a map before you point an agent at it.** Graphlit reads an existing repository (routes, services, tables, jobs and the calls between them) and produces a typed graph from it, with no drawing required. From there you can declare the rules that matter and scope work against real structure instead of guessing. *[Interactive demo: An existing repository, imported into a typed graph. The starting point, not the output.]* Everything else in this article is easier once the map exists, and most of it is guesswork until it does. ## Frequently asked ### Can AI coding tools work on large legacy codebases? Yes, with scoping. The failure mode is not code quality but blast radius: an unscoped change touching files nobody expected. Naming the files a task may modify converts review into a question with an objective answer. ### What is brownfield development? Working on an existing codebase rather than starting fresh. The distinguishing constraint is that the system already works, so the risk lives in unintended change rather than in failing to produce something. ### Should I let an AI agent refactor my whole codebase at once? No. Large refactors are exactly where verification is weakest, because the diff is too big to review and the tests were written for the old structure. Do it in scoped steps, one per commit, each independently revertable. ### How do I give an AI tool context about my existing architecture? A prose description helps but competes for attention and cannot be checked. The stronger approach is structural: a machine-readable map of the components and their relationships, plus dependency rules that fail a build when violated. --- # The thousand-line prompt problem > Every team using AI agents seriously ends up with a giant instructions file. It works, briefly, and then stops, for a reason worth understanding. **Source:** https://graphlit.co/blog/thousand-line-prompt **Published:** 2026-08-09 The progression is almost universal. You start with a sentence. The agent does something you did not want, so you add a line explaining not to. That happens again. Six weeks later there is a file in your repository with four hundred lines of accumulated instruction, and someone on the team refers to it, not entirely as a joke, as the constitution. This is a reasonable response to a real problem, and it does work, up to a point. The point is closer than most teams expect. ## Why the file grows Each line in it is a scar. Something went wrong once, and the line is there to stop it happening again: - `Always use the existing validation helper, do not write a new one.` - `Never call the payments service from a component.` - `Database migrations go in the migrations folder, one per change.` - `Do not add a dependency without asking.` Every one is sound. Every one is also **a rule about the architecture, written in English, stored where nothing can check it.** ## The three ways it caps out ### 1 · Attention is finite A four-hundred-line instruction file competes with the actual task for the model's attention. Rule 200 gets less weight than rule 3. As the file grows, the marginal rule does less, and eventually adding rules stops helping measurably. ### 2 · English is not checkable "Never call the payments service from a component" is a rule a machine could enforce trivially, if it were expressed as a rule rather than as a sentence. As prose it is a *suggestion with good intentions*. Nothing fails when it is violated. You find out in review, if the reviewer remembers it exists. ### 3 · It describes without depicting The file tries to convey the shape of a system in paragraphs. Architecture is a graph of parts and the connections between them, and prose is a bad encoding for graphs. This is why the file keeps needing new lines: each one patches a case the previous prose failed to imply. > **The tell** > > You have hit the cap when you add a rule to stop a behaviour, and the behaviour happens again anyway. At that point the file is no longer a control. It is a record of things you wish were controls. ## The instructions file is doing two jobs Separating them is most of the fix, because they want completely different homes. | Job | Example | Belongs in | | --- | --- | --- | | Orientation | "The API is in this folder, tests run with this command" | Prose. This is genuinely what a text file is for. | | Constraint | "Components must never import payments directly" | A machine-checkable rule that fails a build. | | Structure | "These are the services and how they connect" | A typed graph, generated from and compared to the code. | Keep the first. It is short, stable, and rarely needs updating. It is the second and third that grow without limit when they are trapped in prose, and both have better encodings. ## Replacing prose with structure 1. **Extract the constraints**: Go through the file and mark every line that is really a rule about what may talk to what. In most files this is a third to a half of the content. 2. **Make them fail something**: Dependency-rule tooling exists for most ecosystems and will enforce module boundaries directly. A rule that fails a build does not need to be repeated to anyone, ever. 3. **Give the structure a real form**: The parts of the system and their connections belong in a typed map that is derived from the code, not in a paragraph that describes it from memory. 4. **Keep what is left**: What remains is orientation: genuinely useful, and now short enough that a model reads all of it. Graphlit is built on that split. The architecture is a drawing, which becomes a typed graph. Rules are declared **on the graph** and enforced on every change: this may only be reached through that, these two must never talk directly, this layer may not call that one. What is left over for prose is the orientation, which is a page rather than a constitution. *[Interactive demo: Rules declared on the graph, checked against the code rather than asked for politely.]* > A rule you have to repeat is not a rule. It is a hope with a paragraph number. ## A note on the irony Graphlit exists to delete the thousand-line prompt, which would make it embarrassing for this site to need a thousand words per page to explain itself. There is a word budget in the build that fails if a marketing page goes over. The articles are exempt, because someone searching for a comparison wants the full answer. The general principle holds either way: if something has to be explained at length repeatedly, the explanation is not the fix. The structure is. ## Frequently asked ### Are AI instruction files like CLAUDE.md or cursor rules a bad idea? No. They are genuinely useful for orientation: where things live, how to run the tests, what the conventions are. The problem is only when they take on architectural constraints, because prose cannot enforce anything and the file grows without limit. ### How long should an AI instructions file be? Short enough that you would expect someone to read all of it. If it has grown past that, the excess is usually constraints that want to be automated checks rather than sentences. ### Why do AI agents ignore instructions? Rarely outright defiance. Usually attention. A long instruction file competes with the task itself, so later rules carry less weight. If a rule matters enough that ignoring it breaks something, it should fail a build rather than live in a paragraph. --- # How to review a codebase you didn't write > Inheriting a codebase you did not write is now normal, and the reviewer is often the person who prompted it. Here is an order of operations that finds the expensive things first. **Source:** https://graphlit.co/blog/review-ai-generated-code **Published:** 2026-08-09 Reviewing generated code is a different job from reviewing a colleague's pull request. A colleague's diff is small, and they can explain it. Here you have the whole system at once and no author to ask. So the goal is not to read everything (you will not) but to find the small number of places where a mistake would be expensive, and look hard at those. ## Do these first: five checks in twenty minutes Before reading a single line of logic, establish whether the basics hold. Any of these failing is a bigger finding than anything you would discover by reading code. 1. **Does it build from a clean checkout?**: Clone into an empty directory and follow the README. If it does not build without undocumented steps, nothing else you learn is reliable: you are reviewing a system nobody else can reproduce. 2. **Is every secret out of the repository?**: Search the history, not just the current files, for keys and tokens. Generated code frequently inlines a key during development and a later commit removes it from the working tree but not from history. 3. **What happens on the auth routes?**: Find every endpoint and ask which ones check who is calling. The common generated failure is not a missing login page. It is a login page plus one API route out of thirty that forgot the check. 4. **Where does user data live and leave?**: Which tables hold personal data, and every place it is sent outward: logs, analytics, third-party calls, error reporters. Generated error handling is a frequent accidental exfiltration route. 5. **Is anything actually tested?**: Not coverage percentage. Just: do tests exist, do they run, do they fail when you deliberately break something. A suite that passes with the logic removed is worse than none. > **The single highest-yield check** > > List every route or endpoint, and next to each write who is allowed to call it. Then verify each one enforces that. Missing authorisation on one endpoint is the most common serious defect in generated applications and the easiest to check exhaustively. ## Then: map before you read The instinct is to open `main` and start reading. Resist it. You will spend an afternoon in files that do not matter. Instead, build a map. You are answering four questions: 1. What are the parts? Services, pages, routes, jobs, tables. 2. What calls what? The edges matter more than the boxes. 3. Where does data enter and leave the system? 4. What is duplicated? Are two things doing the same job? Question four is where generated codebases differ most from human ones. Humans duplicate when they are rushed; models duplicate when they cannot find the existing implementation, which is often. *[Interactive demo: Deriving the map mechanically rather than by reading: routes, services, tables and the calls between them.]* Doing this by hand on a large repository takes a day or two. Graphlit does the import automatically, which is the point: the map is the expensive part of the review, and it is also the part a machine can do. ## The questions that expose real problems Once you have a map, these five questions find more than line-by-line reading will. | Question | What a bad answer means | | --- | --- | | Which parts talk to the database? | If the answer is "lots", there is no data layer and every schema change is now a search-and-replace across the codebase | | How many ways can a user be authenticated? | More than one means at least one is probably weaker, and you have found the way in | | What happens when this external call fails? | Generated code is optimistic by default; unhandled failure paths are the most common cause of a confusing outage | | Which code is unreachable? | Orphans mean changes routed around old code rather than replacing it, a reliable sign of drift | | What is duplicated? | Two implementations means a fix applied to one and not the other, which is a bug with a delay fuse | ## What not to bother with Reviewing generated code has its own list of wasted effort: - **Style and formatting.** It is consistent, because a machine wrote it. Run a formatter and stop thinking about it. - **Micro-optimisation.** Generated code is rarely fast and almost never the bottleneck. Measure before caring. - **Naming debates.** Real cost, wrong time. You are looking for correctness and structure. - **Reading every file.** In a large generated codebase this is neither possible nor useful. Follow the map to the risky parts. ## Write down what you found The review's real output is not a list of bugs. It is **the map**, and the rules you discovered while making it. If you finish an audit and the only artefact is a fixed bug list, the next person repeats your entire afternoon. If you finish with a diagram of the parts and a written statement of which parts may talk to which, you have converted one-off effort into something durable, and if those rules are machine-checkable, into something that stays true. See [architecture drift](https://graphlit.co/blog/architecture-drift) for why that last property matters. ## Frequently asked ### How long should a codebase audit take? The five fast checks take under an hour on most projects. Building a useful map takes one to two days by hand for a medium codebase, or minutes if it can be derived automatically. Reading everything is not a realistic goal and not the objective. ### What are the most common problems in AI-generated codebases? In rough order of frequency: a missing authorisation check on one endpoint among many, duplicated logic where the model could not find the existing implementation, unhandled failure paths on external calls, and secrets committed to git history. ### Can I review code if I'm not a developer? You can do the structural checks: does it build from a clean checkout, are there secrets in the repository, is there a list of endpoints with stated permissions, do tests exist and fail when something is broken. Those find real problems. The logic itself needs someone who can read it. ### Should I rewrite AI-generated code that works? Usually no. Rewriting discards working behaviour along with the bugs. Map it, enforce the boundaries you care about, and replace parts only where you have a concrete reason. A rewrite is the most expensive response to a comprehension problem. --- # No-code vs AI builders vs hiring a developer > Four routes to the same destination, with very different bills. The one that matters is not the upfront cost. It is what you own at the end. **Source:** https://graphlit.co/blog/no-code-vs-ai-builders **Published:** 2026-08-09 If you need software and cannot write it yourself, you have four realistic options. Each is genuinely correct in some situations, and each has a cost that shows up later rather than sooner. ## The four routes | Route | Time to first version | What you own | The later cost | | --- | --- | --- | --- | | No-code platform | Days | A configuration inside their product | Leaving means rebuilding from zero | | AI app builder | Hours to days | Source code, if the tool exports it | Nobody understands the code, including you | | Agency or freelancer | Weeks to months | Source code, and a handover document | Every change needs them, at their rate | | In-house developer | Months | Everything, including the knowledge | Salary, and the risk they leave | ## No-code platforms Tools in this family let you assemble an application from visual components without writing code. They are excellent, and the reflexive developer sneer at them is mostly wrong. **Choose this when** your app looks broadly like other apps (forms, records, dashboards, workflows, a marketplace) and speed matters more than control. Plenty of real businesses run on them profitably for years. **The catch is the exit.** You are not building software so much as configuring theirs. If you need something the platform does not do, or the pricing changes at your scale, there is usually no migration path, only a rebuild. That is a manageable risk if you go in knowing it, and an unpleasant surprise if you do not. ## AI app builders Describe the app, get source code. Faster than no-code for anything non-standard, and crucially the output is real code rather than a proprietary configuration. See the [detailed comparison](https://graphlit.co/blog/ai-app-builders-compared) of the main options. **Choose this when** you want to own the output and your idea does not fit a no-code template. **The catch is comprehension.** You own a codebase nobody has read. That is fine while the tool can keep changing it for you, and it becomes a problem the first time it cannot, because your options are then to hire someone to understand it from scratch, or start again. > **Owning code you cannot read is not the same as owning software** > > This is the trap specific to this route, and it is easy to miss because it looks like the good outcome. You have the files. You have the repository. What you do not have is the ability to answer "what will break if we change this?", and that ability is most of what ownership is actually worth. ## An agency or freelancer Pay professionals to build it. Slower and more expensive per feature, and you get something a person can explain to you. **Choose this when** the software is the business, the domain is complicated, or something regulated is involved. There is no substitute for a human who is accountable for the decisions. **The catch is the handover.** Code arrives; understanding usually does not. Six months on, a small change means either going back to them or paying someone new to learn a codebase from scratch. Ask what documentation you get, and ask specifically for an architecture description rather than API docs. ## An in-house developer The most expensive and the most complete. You get the code and the person who understands it. **Choose this when** software is your product and you expect to change it continuously for years. **The catch is concentration.** For a long time it is one person, and everything they know is undocumented. That is a real business risk, and the mitigation is the same as everywhere else on this page: insist the architecture is written down somewhere that is not a person. ## The question that actually decides it Not "what does it cost?" but: **who will be able to change this in a year, and how will they know what it does?** 1. **Validating an idea, might delete it**: No-code or an AI builder. Optimise for finding out whether anyone wants it. Do not buy insurance on a building you may not keep. 2. **It works and now it has to last**: Get to owned source code, in version control, with the architecture written down. This is the transition most projects handle badly. 3. **Regulated, or handling money or health data**: Hire a professional for those parts, whatever you use elsewhere. Generated auth and payment code is where a plausible mistake is most expensive. 4. **Software is the business**: Hire in-house, and make architecture documentation a condition of the role rather than a nice-to-have. ## Where Graphlit fits Squarely at the second step: the transition from "it works" to "it has to last". You draw your system as boxes and arrows, which is a thing you can do without being able to code. Graphlit turns that into a typed map, builds against it, and keeps checking that the code still matches. When you later hand it to a developer, an agency, or a new tool, you hand over a description of the system rather than a pile of files and an apology. *[Interactive demo: A drawing becomes a typed architecture map. No code required to produce it.]* It does not replace hiring someone when you need someone. It changes what you can hand them. ## Frequently asked ### Is no-code cheaper than hiring a developer? Much cheaper upfront and often more expensive at scale, because platform pricing usually grows with usage and leaving means rebuilding. For validating an idea it is almost always the right economic call; for a system you expect to run for years, model the cost at your projected scale before committing. ### Can I switch from no-code to real code later? Rarely as a migration. Usually as a rebuild. Most platforms do not export anything a developer can continue from. If you think you will outgrow one, that is an argument for a route where you own source code from the start. ### How do I know if generated code is any good? Without being able to read it, you check properties rather than quality: is it in version control, does it build from a clean checkout, are there tests, is there a description of the architecture, and can an independent developer explain it back to you in an hour. Any of those failing is a real signal. ### What should I ask an agency before signing? Who owns the code and the repository, what documentation is delivered, whether the architecture is described anywhere other than in their heads, and what a typical change costs after the project ends. The last two predict your real total cost better than the quote does. --- # Vibe coding, and the bill that arrives later > Building by feel (prompt, look, accept, repeat) is the fastest software has ever been to write. The cost is real, deferred, and avoidable. **Source:** https://graphlit.co/blog/vibe-coding-bill **Published:** 2026-08-09 Vibe coding is building by feel: describe what you want, glance at the result, accept it if the screen looks right, repeat. You are steering on outcomes rather than reading the code. It is not a joke and it is not lazy. For a large class of work it is the correct strategy, and treating it as beneath serious engineers is a way of missing what has actually changed. ## What it is genuinely good for - **Exploration.** When you do not yet know what you are building, building five versions badly beats specifying one carefully. - **Throwaway work.** Prototypes, demos, internal one-offs, spikes. Debt on something you delete is never repaid. - **Unfamiliar territory.** Getting something working in a stack you have never used, to find out whether the approach is viable at all. - **Interfaces.** Screens are self-evidently right or wrong. Looking at the result *is* a legitimate check. > **The honest summary of the upside** > > For anything short-lived or exploratory, vibe coding is not a compromise. It is simply the fastest correct approach. The entire cost model below depends on the code surviving, so if it will not survive, the cost is zero. ## When the bill arrives The bill is not a crash. It arrives as a specific, recognisable moment. > Something is broken in production, and you are reading your own codebase for the first time. That is the entire cost, and everything else is a consequence of it. You have a system you own, that works, that you cannot navigate, so diagnosis means learning the code under time pressure, in the least forgiving circumstances available. The secondary costs follow from the same root: | Moment | What it costs | | --- | --- | | First production incident | Learning the codebase during an outage instead of before one | | First security question | Nobody can answer "where is user data handled?" without a search | | First hire | Onboarding with no architecture to explain, so they read files and guess, like the model did | | First audit or customer security review | Questions phrased in terms of the system's structure, which nobody has written down | ## Why "just read the code later" does not work It sounds like a fair plan, and it fails for a practical reason: **reading code tells you what it does, never what it was for.** You can read a function and understand its behaviour exactly. What you cannot recover by reading is whether the duplicated validator is a deliberate distinction or an accident, whether the retry loop is load-bearing, whether that direct database call was a considered exception or a shortcut nobody noticed. That information existed only in the moment the code was generated, and vibe coding is defined by not capturing it then. This is the same mechanism behind [why AI-generated apps fall apart](https://graphlit.co/blog/why-ai-generated-apps-rot): you are now in the position the model was in. ## Keeping the speed, losing the hangover The fix is not to stop vibe coding. It is to capture structure *while* you go, at a cost low enough that it does not slow you down. 1. **Draw before you prompt**: Five minutes of boxes and arrows before a feature. Not a formal document, just the parts and what talks to what. This is the artefact that survives, and it is the one nobody makes. 2. **Version control from minute one**: Not for the history so much as for the diff. Being able to see exactly what a change touched is the cheapest comprehension tool that exists. 3. **Name your boundaries out loud**: Decide the two or three rules you actually care about (what must never call what) and enforce them mechanically. Two enforced rules beat twenty written down. 4. **Draw a line around the risky parts**: Auth, payments, anything touching personal data. Those get read by a human, whatever you do elsewhere. This is the highest-value hour you will spend. That first step is what Graphlit is built around. You draw the architecture, it becomes a typed graph, and the code is generated against it, so the structure exists as a by-product of building rather than as documentation you were supposed to write afterwards and did not. *[Interactive demo: The drawing is the artefact. It is also the thing the build is checked against.]* The point is not discipline for its own sake. It is that when the incident comes, and it does, you are reading a map instead of reading forty files. ## Frequently asked ### Is vibe coding suitable for production applications? For getting to production, often yes. For staying there, it depends entirely on whether anyone can navigate the result under pressure. The deciding factor is not code quality but comprehension: whether someone can answer "where does this happen?" during an incident. ### What is the main risk of vibe coding? Owning a working system nobody understands. It is not usually experienced as bad code; it is experienced as an outage where diagnosis means reading your own codebase for the first time, at the worst possible moment. ### How can I vibe code more safely? Sketch the structure before prompting, keep everything in version control, mechanically enforce two or three boundaries you actually care about, and have a human read anything touching authentication, payments or personal data. --- # Architecture diagrams that stay true > The diagram on the wall was accurate the day it was drawn. Here is why that is the last day it was, and what to do differently. **Source:** https://graphlit.co/blog/diagrams-that-stay-true **Published:** 2026-08-09 Every engineering organisation has the same artefact: a diagram, made for a presentation eighteen months ago, that everyone knows is out of date and nobody updates. It still gets shown to new starters, because it is better than nothing, and it teaches them a system that no longer exists. ## Why diagrams die Not laziness. Three structural reasons, and none of them is fixed by resolving to try harder. ### They are disconnected from the thing they describe The diagram lives in a design tool. The system lives in a repository. Nothing links them, so changing one has no mechanical relationship to the other. Two artefacts, one intention, no connection. ### Updating them is unrewarded work Shipping a feature is visible. Updating a diagram afterwards is invisible, and skipping it has no immediate consequence. Any process that depends on people doing invisible work with deferred payoff will lose. ### They are pictures, not data A box in a design tool is a rectangle with a label on it. It does not *know* it is a service. Nothing can ask it what it connects to, so nothing can check it, which means nothing can tell you it has gone stale. > **A wrong diagram is worse than no diagram** > > With no diagram, people read the code. With a wrong one, they act on it: planning against a service that was decommissioned, assuming a boundary that was crossed a year ago. The confidence is the damage. ## The three approaches | Approach | How it works | Stays true? | Trade-off | | --- | --- | --- | --- | | Draw it by hand | A design tool, updated manually | No, it decays from day one | Total expressive freedom; zero enforcement | | Diagrams as code | Text definition rendered to an image, in the repo | Partly: versioned, still manual | Reviewable in a diff, but nothing checks it against reality | | Generated from code | Derived by static analysis | Yes, regenerated each time | Shows what *is*, never what was *intended* | The third is the biggest step forward and it has a real limitation worth naming. A generated diagram cannot be wrong, but it also cannot disagree with you, and disagreement is the useful part. It will happily draw the direct database call the frontend should never have made, as a normal edge, because it is describing reality with no opinion about it. ## What a diagram needs to be trustworthy Four properties. The first three are common; the fourth is the one that changes what a diagram is for. 1. **Typed.** A box is a *service* or a *table* or a *route*, not a rectangle. Type is what makes a connection meaningful and checkable. 2. **Versioned with the code.** In the repository, changing in the same commits, reviewable in the same diff. 3. **Comparable to reality.** Something can mechanically ask whether the code still matches it. 4. **Opinionated.** It states what *should* be true, so reality can contradict it, and that contradiction is the alert. Properties three and four together are what separate a picture from a contract. A generated diagram has three. A hand-drawn one has four and nothing else. You want both. ## Intent and reality, side by side This is the design Graphlit follows. You draw the intended architecture. That is the opinionated half, and drawing is a good interface for it because architecture is genuinely a picture. It becomes a **typed graph**: services, routes, tables, jobs, and the edges between them, each with a type. Then the code is read and mapped back onto that graph, file by file. Now there are two structures of the same kind, and comparing them is mechanical: what exists in the code with no counterpart in the drawing, what is drawn but unbuilt, and which files changed underneath the node that owns them. *[Interactive demo: A typed graph: boxes with kinds, edges with meaning. Not a picture of a system, a description of one.]* The diagram stops being documentation and becomes an assertion. Nothing has to remember to update it, because the moment it stops being true, something says so. That is the mechanism behind [catching architecture drift](https://graphlit.co/blog/architecture-drift). ## If you are not going to adopt a tool Most teams will not change their tooling this quarter. Three things that help regardless: 1. **Move the diagram into the repository**: Even as a text-defined diagram rendered to an image. Being in the same pull request as the change is most of the battle. It puts the update in front of a reviewer at the moment it is wrong. 2. **Draw fewer, smaller diagrams**: One diagram of everything is never accurate and never read. One per subsystem, each fitting on a screen, gets updated because updating it is a two-minute job. 3. **Automate the boundaries you care about**: You will not enforce a whole diagram by hand. You can enforce three dependency rules with existing tooling, and those three are usually the ones that matter. ## Frequently asked ### What is the best tool for architecture diagrams? For communication, whatever your team already uses. For accuracy, the deciding property is not the tool but whether the diagram is connected to the code: versioned alongside it and checkable against it. A beautiful diagram in a disconnected design tool decays exactly as fast as an ugly one. ### Should architecture diagrams be generated or hand-drawn? Both, for different jobs. Generated diagrams show what the system actually is and cannot go stale. Hand-drawn ones state what it should be, which is the part that can be violated. The useful setup is a stated intent plus a generated reality, compared automatically. ### How often should architecture documentation be updated? Any answer in units of time is the wrong shape. It means the update is a scheduled chore that will be skipped. It should be updated in the same change that alters the system, which only happens realistically if something fails when it is not. ### Is the C4 model still worth using? Yes, as a way of thinking about levels of detail: separating context, containers and components stops the single unreadable everything-diagram. It is a notation, not a mechanism, so it does not solve staleness on its own; pair it with something that checks the diagram against the code. --- # Staying in control of software you can't read > Control does not come from reading code. It comes from being able to check specific things, and every one of them is available to you. **Source:** https://graphlit.co/blog/non-technical-founder-control **Published:** 2026-08-09 If you cannot read the code, the usual advice is to trust your developer or your tool. That is not control, it is hope with extra steps. And the alternative is not learning to program. Control comes from being able to verify **properties** of the system rather than its contents. You can check every one of the things below without reading a line. ## The five things you must own Not documents to file away. Things whose absence is a genuine emergency. 1. **The repository, in your account**: The code lives in an organisation you own, on a service you pay for, with you as owner. Not on a contractor's personal account, not only on a laptop. This is the single most common and most damaging gap. 2. **The ability to deploy without one specific person**: Written instructions for putting a new version live. If only one human can ship, you do not have a product. You have a dependency with a notice period. 3. **The accounts, in your name**: Domain, hosting, database, email provider, payment processor. Every one registered to a company address you control, with billing you can see. 4. **A description of the architecture**: What the parts are, and what talks to what. One page or one diagram. Not API documentation: the shape of the system. 5. **A record of what stores personal data**: Which parts hold customer information and where it goes. You are legally accountable for this regardless of who built it. > **Check the first one today** > > Log in to the code hosting service and confirm your organisation owns the repository and you are listed as owner. Founders discover the answer is no at the worst possible moment: during a dispute, a departure, or an acquisition. It takes two minutes to check. ## Questions that work without technical knowledge Each of these has a good answer and a bad one, and you can tell the difference without understanding the details. | Ask | Good answer sounds like | Worry if | | --- | --- | --- | | "Can you show me the system on one page?" | They draw it in two minutes, confidently | It takes days, or it comes back as a wall of text | | "If you disappeared, what would break first?" | A specific, honest answer | "Nothing, it's all documented", which no system ever is | | "What happens if this change is wrong?" | "We revert it, here is how" | Uncertainty, or a long pause | | "Where does customer data live?" | A short, exact list | A general answer about the database | | "What are you worried about?" | Something real and specific | "Nothing", which is the biggest red flag on the list | The last one carries the most information. Every competent engineer has a list of things that worry them. Someone who claims none is either not looking or not telling you. ## Checks you can run yourself Genuinely doable without technical skill, and they find real problems. - **Make two accounts and try to see the other's data.** Sign up twice, log in as the first, and attempt to reach the second's information by changing what you see in the address bar. This finds real permission bugs and needs no expertise. - **Ask for a change to be reverted.** Pick something small and recent and ask for it to be undone. If that is hard, the ability to recover from a bad change does not exist. - **Check the last time the map was updated.** If the architecture description has not changed while the product has, it is now fiction. - **Ask what a new developer would need on day one.** A team that can answer crisply has a system someone else can take over. One that cannot has a system that depends on the people currently in it. ## The warning signs that matter Not "the code is messy": you cannot judge that, and it matters less than people think. These are the ones that predict trouble: 1. **Estimates growing for similar work.** A change like one that took two days last quarter now takes two weeks. This is the clearest measurable signal that the system has become hard to reason about. 2. **"We should rewrite it."** Sometimes true, usually a symptom that nobody understands the current system well enough to change it safely. 3. **Nobody wants to touch a particular part.** There is a file or an area people route around. That is where the next incident will come from. 4. **Changes break unrelated things.** The strongest signal of all. It means the parts are connected in ways nobody has mapped. ## Why this is harder now, and also easier Harder, because AI-generated code arrives faster than anyone can review it, and it looks tidy while being structurally confused. The old proxy (*does the code look well kept?*) has stopped working. See [technical debt from AI coding](https://graphlit.co/blog/ai-technical-debt). Easier, because the same shift makes structure *checkable by machine*. Questions that used to require a senior engineer's afternoon (what are the parts, what calls what, is anything duplicated, does the code still match the design) can now be answered automatically. That is what Graphlit is for, from your side of the table: the drawing is something you can read, and it is mechanically checked against code you cannot. When they stop agreeing, you get told, which means "is the system still what we agreed?" becomes a question with an answer rather than a matter of trust. *[Interactive demo: The drawing you can read, checked against the code you cannot.]* None of this replaces trusting the people you work with. It means you are not relying on trust alone for things that can be verified. ## Frequently asked ### How can a non-technical founder manage developers effectively? Verify properties rather than judging code. Own the repository and accounts, insist on a one-page architecture description, ask what would break if a specific person left, and watch whether estimates for similar work are growing. All of that is available without technical skill. ### What should I ask for at the end of a development contract? Owner access to the repository and all infrastructure accounts, written deployment instructions someone else can follow, an architecture description, and a list of what stores personal data. Agree these at the start. They are much harder to obtain at the end. ### How do I know if my developer is doing a good job? The most reliable proxy is whether similar work is getting slower over time, and whether a change can be reverted quickly when it turns out to be wrong. Both are measurable without reading code, and both track the health of the system more accurately than code appearance. ### Is it risky to build a company on AI-generated code? It is risky to build on code nobody understands, whoever or whatever wrote it. Generated code makes that easier to end up with by accident, because it arrives faster than review does. The mitigations are ordinary: own the repository, keep an architecture description, and get security-sensitive parts read by a human. --- # Technical debt from AI coding: how to measure it > AI does not produce more technical debt so much as a different kind, one the usual measurements were not designed to see. **Source:** https://graphlit.co/blog/ai-technical-debt **Published:** 2026-08-09 Technical debt was defined as a deliberate trade: take a shortcut now, pay interest later, and repay when you can. The definition assumes somebody *chose*. AI-generated debt breaks that assumption. Nobody chose. It accumulates as a side effect of changes that each looked correct, which is why the standard measurements keep reporting that everything is fine. ## How it differs | | Human debt | AI debt | | --- | --- | --- | | Origin | A deliberate shortcut | An incorrect inference | | Someone knows about it | Usually, and often it is in a comment | No | | Shape | Ugly code that works | Clean code in the wrong place | | Typical form | A hack, marked as one | A duplicate, or a crossed boundary | | Found by | Reading it: it looks wrong | Nothing, until it causes an incident | That third row is the one that defeats existing tooling. Traditional quality tools look for code that *looks* bad: long functions, deep nesting, high complexity. Generated code scores well on all of it. It is well-formatted, consistently named, and reasonably decomposed. It is also, sometimes, the second implementation of something that already existed. > **Clean code is not the same as coherent code** > > A codebase can pass every linter, hold high test coverage, keep complexity low, and still have three ways to validate an email, two paths into the database and a service nothing calls. Every file is fine. The system is not. Quality metrics measure files. ## Four signals that actually work These are structural rather than stylistic, which is why they catch what the usual metrics miss. ### 1 · Duplicate responsibility Count the places that do the same job: validate the same thing, format the same value, call the same external service. Not copy-pasted text, which clone detectors already find, but *semantic* duplication: two different implementations of one responsibility. This is the single most reliable indicator of generated debt. ### 2 · Boundary crossings Count how many distinct components reach a given resource. If eleven places query the database directly, you no longer have a data layer. You have eleven, and any schema change is now a repository-wide search. ### 3 · Orphan rate The proportion of code nothing reaches. Orphans mean changes routed *around* existing code rather than modifying it, which is the characteristic signature of an agent that could not find the right place and made a new one. ### 4 · Map-to-code divergence If you have a stated architecture, the count of things in the code with no counterpart in it, and vice versa. This is [architecture drift](https://graphlit.co/blog/architecture-drift) expressed as a number, and it is the most direct measure available. ## Metrics that mislead here - **Test coverage.** Generated code often comes with generated tests, which can be high-coverage and assert almost nothing meaningful. Coverage measures execution, not verification. - **Cyclomatic complexity.** Models write short, shallow functions. Low complexity with the logic spread across four duplicated paths is worse than one honest function. - **Lines of code.** Volume is no longer a proxy for anything. It costs nothing to produce and says nothing about coherence. - **Static analysis warnings.** Genuinely useful for catching real bug classes, and blind to the structural problems above by design. ## Keeping it visible Debt you can see is manageable. The specific danger of the AI variety is invisibility. Nobody made a choice, so nobody has it on a list. 1. **Measure structure on every change, not quarterly**: The four signals above are cheap to compute and only useful as a trend. A number that appears in a review once a quarter is a report; a number that moves on every pull request is a control. 2. **Treat a new boundary crossing as a review comment**: Not a build failure necessarily, but a change that adds the twelfth direct database caller should say so out loud, because nobody reading the diff will notice on their own. 3. **Give duplication a home**: When the check reports two implementations of one responsibility, the resolution is a decision: merge them, or state why both exist. Either is fine. Silence is not. 4. **Keep the intended architecture written down**: You cannot measure divergence without something to diverge from. This is the prerequisite for the fourth signal and most of the value of the other three. Graphlit computes these structurally: the graph knows which nodes own which files, so duplicate responsibility, orphaned code, unbuilt nodes and boundary violations are queries against a map rather than an afternoon of reading. *[Interactive demo: Structural findings read off the graph: duplication, orphans, crossed boundaries.]* > **These are hypotheses, not verdicts** > > A structural signal says "these two things look like the same responsibility". It does not know your domain, and sometimes two similar-looking validators are correctly separate. The value is in surfacing the question early enough to answer cheaply, not in being right without you. ## Frequently asked ### Does AI-generated code create more technical debt? Not necessarily more, but a different kind. Human debt is usually a known shortcut; AI debt is an unnoticed inference: a duplicate implementation or a crossed boundary. It is harder to manage mainly because nobody knows it is there. ### How do you measure technical debt in AI-generated code? Structurally rather than stylistically: duplicate responsibilities, how many components reach a shared resource directly, the proportion of unreachable code, and divergence between the stated architecture and the actual one. Traditional quality metrics score generated code well while missing all four. ### Is test coverage a good measure of AI code quality? Weakly. Generated tests can achieve high coverage while asserting very little, because coverage measures which lines execute, not whether anything is verified. A better check is whether the suite fails when you deliberately break the logic. --- # From whiteboard sketch to working software > The most accurate description of your system was drawn on a whiteboard and photographed once. Here is what changes if you keep it. **Source:** https://graphlit.co/blog/sketch-to-software **Published:** 2026-08-09 Every system in the world starts the same way. Someone stands at a whiteboard and draws boxes with arrows between them, and for about forty minutes everyone in the room shares a completely accurate mental model of what is going to be built. Then someone photographs it, the photo goes into a chat thread, the board gets wiped, and that shared understanding begins decaying immediately. Within a month the only description of the system is the code, and the code does not explain itself. ## Why the drawing was better than the document It is worth being precise about what is lost, because the instinct to say "we should have written a proper spec" is wrong. The specification would have been worse. - **It was structural.** Boxes and arrows are the actual shape of a system. Prose describing a graph is a lossy encoding of one. - **It was complete at the level that mattered.** Every part, every connection, nothing else. A document would have had four pages about one component and nothing about the rest. - **Everyone could read it.** The designer, the founder, the engineer and the person from finance all understood the same picture. Almost nothing else in software has that property. - **It was fast.** Forty minutes. Which is why it actually got made. > The whiteboard is the highest-bandwidth architecture tool we have, and we throw the output away every single time. ## What has to be true for a sketch to be more than a picture Photographing it is not enough. That just gives you a stale picture in a slightly more durable format. Three things have to change. 1. **The boxes need types**: A rectangle labelled "Payments" is a rectangle. A node that knows it is a *service*, connected to something that knows it is a *table*, is data, and data can be checked. Type is what turns a drawing into a description. 2. **It has to be connected to the code**: Each box has to know which files implement it. Without that link there is no way to ask whether the drawing is still true, and a drawing nobody can check is a drawing that will quietly stop being true. 3. **The check has to be automatic**: Not a quarterly architecture review. Something that runs after every change and reports what no longer matches, because divergence found in week twelve has already cost what it was going to cost. > **This is the difference between documentation and a contract** > > Documentation describes and hopes. A contract states what must be true and something checks it. The same drawing can be either. What decides it is whether anything is comparing it to reality. ## What that makes possible Graphlit is built around exactly this. You draw the architecture on a whiteboard, on paper, or on a canvas, and it becomes a typed graph: services, routes, tables, jobs, and the edges between them. Photographs work too. They are read by deterministic offline code (shapes, arrows, handwriting) and only reach a vision model if that read fails, because a picture of a whiteboard is often somebody's unreleased architecture. *[Interactive demo: Drawing, typing, planning, building, verifying, and checking the drawing again.]* Four things follow from the drawing being real data rather than an image: | Because the sketch is typed | You get | | --- | --- | | Every box knows what kind of thing it is | The gaps you did not draw (auth, sessions, jobs) can be inferred and shown as suggestions you can delete | | Every box maps to files | A build plan where each task names the files it may touch, so a change that strays is stopped rather than reviewed | | The graph can be compared to code | Drift becomes a report: what moved, what vanished, what was never built | | Rules can be attached to edges | "Nothing reaches the database except through the API" becomes a check instead of a hope | ## The part that does not change Being honest about the boundary matters more than the pitch. A drawing that is faithfully implemented and continuously verified can still be **the wrong drawing**. If the architecture is a bad idea, checking it rigorously produces a rigorously verified bad idea. None of this replaces the judgement about what to build. It only guarantees that what you decided is what you got. That turns out to be a large share of the problem in practice. Most systems do not fail because someone designed them badly at the whiteboard. They fail because the thing that was built slowly stopped resembling what was agreed, and nobody noticed until it mattered. That is [architecture drift](https://graphlit.co/blog/architecture-drift), and it is the failure this is aimed at. ## Start with the forty minutes Whatever you build with, the highest-value thing you can do before writing code is still the oldest one: draw the parts and the arrows between them. The only change worth making is not wiping the board afterwards. Everything above is a way of taking that seriously. If you want the drawing checked against the code automatically, that is what we are building. You can [create an account](https://graphlit.co/get-started) and try it. ## Frequently asked ### Can you generate an application from a diagram? Yes, provided the diagram is typed rather than a picture: nodes that know whether they are a service, a table or a route, connected by edges with meaning. An image of a diagram carries none of that, which is why photographing a whiteboard does not give you anything a machine can build from. ### What makes a good architecture sketch? One box per part, one arrow per connection, and every box named after something real. Fitting on a single screen is a useful discipline. If it does not fit, you are drawing two diagrams at once. Level of detail matters far less than completeness at whatever level you chose. ### Do I need to be technical to draw my system's architecture? No. Boxes for things that exist and arrows for what talks to what is a description anyone who understands their own product can produce. The technical vocabulary is a naming convention, not a prerequisite. ### How is this different from UML or model-driven development? Earlier model-driven approaches tried to generate complete systems from exhaustive formal models, and foundered on the effort of keeping the model complete. The difference here is scope and direction: the graph describes structure rather than behaviour, and it is continuously compared against real code rather than treated as the source everything is generated from once. ---