How to Develop as a Non-Developer

Specs, behavioral verification, and shipping software you can’t read
In the age of building with coding agents, how do we ship something without it quietly, inexplicably going wrong weeks later?
Start with the honest constraint. A developer working with a coding agent verifies the work by reading it. You can’t. Not because you’re not capable, but because reading code fluently takes years of practice you haven’t spent, and no amount of enthusiasm substitutes for it.
So, you need a different method, and it comes down to one substitution: you cannot verify the code, so you must verify the behavior. Done properly, that’s not a consolation prize. Behavioral verification is more rigorous than most code review, and it’s mostly a thinking-and-writing job — which means it’s fully available to you.
You also have an advantage that gets overlooked. You usually know what the answer should be. An analyst knows what last quarter’s number was. Someone in operations knows what a valid order looks like and what a broken one looks like. That domain knowledge is an answer key, and most developers building the same tool wouldn’t have it. It is the foundation everything below is built on.
1. Write the specification first
“Build me an app” fails for a predictable reason: it hands the agent an empty canvas, and an agent given an empty canvas fills it with the most generic, average version of what you described. You end up with something that technically matches your words and doesn’t match your problem. The fix isn’t a better one-line request — it’s replacing the one line with a document.
Writing that document is a thinking-and-writing task, not a coding task. Non-developers frequently write better specs than developers do, for the simple reason that they’re closer to the actual problem.
A good spec also prevents a second, slower-moving failure: session drift. Without a spec document, the only context every new session starts with is the codebase. There is no memory of previous decisions and why they were made, leading to code accumulation with no overall plan. The spec is what the agent and you can return to instead of re-deciding the shape and logic of the solution every time you ask for something new.
Write this before you ask for a single line of code. Plain English throughout. No technical vocabulary required.
SPEC: <what I'm building>
PURPOSE
What problem this solves, and who uses it.
INPUTS
Where the data comes from, what format, how much, how often.
Who owns that data today.
OUTPUTS
What comes out, in what format, and who receives it.
WHERE THINGS LIVE
Where data is stored, and who can see it.
WHEN THINGS GO WRONG
For each case: what should happen?
- a required field is missing
- the source file is empty, or twice the usual size
- a value looks obviously wrong
- the same input arrives twice
MUST NEVER
Things that would be unacceptable no matter what.
ACCEPTANCE EXAMPLES <- the most important section
Real inputs with the correct answers, which I already know.
Input Expected output
--------------------------- -----------------------------
Mar 2026 invoice file Total 148,220.50 / 1,204 rows
Invoice with a credit note Credit subtracted, not added
...
OUT OF SCOPE
What this is explicitly not for.
OPEN QUESTIONS
What I haven't decided. Never let the agent decide these silently.
The acceptance examples deserve special attention. Pull five to ten real cases out of work you have already done by hand — a report you produced last quarter, a spreadsheet you built, invoices you processed manually. Include the awkward ones: the refund, the duplicate, the month with the extra billing period. This table is your answer key, and in section 3 it becomes your test suite. It is the single highest-leverage document you will produce in the whole project.
The spec doesn’t need to be complete for the entire project before you start building — thinking through every bit of logic or edge case in advance is an impossible feat. This is an iterative document, so start small. Write enough to cover the first piece you’re building, get that working, then come back and extend the spec before you extend the build.
If your spec starts growing too large to manage, leave it in your repository, mark it complete and immutable, and start a fresh one for the next phase of work. One rule matter more than the others: specs should only be updated by you, based on how you expect the solution to behave.
Never let the agent update the specification to match the code.
2. Understand the system, not the syntax
You are not going to understand line 47, and chasing that is a waste of your time. But there is a level of understanding that’s both reachable and sufficient, and it’s the level at which most real problems actually live.
You should be able to answer all of these about anything you build:
Where does the data come from, and who owns it?
Where does it get stored — and is that somewhere it’s allowed to be?
Who can see it?
What happens when it fails? Does it stop loudly, or keep going quietly?
What does it cost to run, and does that scale with volume?
What does it depend on that could change without warning — a file format, a website layout, someone else’s spreadsheet?
How would I find out if it stopped working?
You can get most of this by asking:
“Draw me the flow of data through this, step by step, in plain English. List everything it depends on that’s outside my control. Tell me the three most likely ways this breaks in the real world.”
The other half of this section is domain alignment, and it’s where you outperform. The agent has never met your client. It knows how a generic reconciliation job works; it does not know that this client’s fiscal calendar ends on the 26th, or that a negative quantity means a return rather than an error. Code that is generically correct and wrong about your business will pass every technical check and still be wrong. You are the only person in the loop who can catch that.
3. Verify behavior, not code
The “it works” trap
Code that runs without an error message is not necessarily code that’s correct. The failures that actually hurt are silent: a merge that quietly drops 40 rows, a date shifted by a timezone, a rounding rule applied at the wrong step, a filter that excludes a category nobody remembered. The output looks entirely plausible. Nothing turns red. Somebody finds it a quarter later, in front of a client.
Your goal is not just to make the software work. It is also to make silent wrongness loud.
Why the agent’s tests can’t be trusted on their own
The agent writes the code. Then the agent writes tests for the code it just wrote. Tests written after the fact, by the author, tend to check what the code does rather than what it should do. If the code has a bug, the test quietly enshrines the bug and reports green forever.
Ten countermeasures follow. None of them require you to read code. They require you to ask for the right evidence and to know what a hollow answer looks like.
The countermeasures
Countermeasure | What it catches | What you ask for |
Write the answer key first | Tests reverse-engineered from the code instead of from the requirement. | “Turn every row of my acceptance examples into an automated test. Write them before any implementation, then run them and show me that they all fail.” |
Break the code on purpose | Tests that stay green while the code is genuinely broken. | “Introduce one deliberate bug into the discount calculation. Run the full suite and show me the output. Then revert it.” “Now set up mutation testing. Report the score, and list the bugs the tests failed to catch in plain English.” |
Get a blind second opinion | Code that has quietly drifted from the spec — and ambiguity in the spec itself. | “Here is a specification. Write a thorough test suite from it. You have not seen the implementation and must not ask for it — test only what this document promises.” |
Invite a hostile reviewer | Edge cases nobody thought to write down. | “You are a QA engineer whose job is to reject this. Find inputs that break it: empty files, duplicate rows, wrong encoding, unexpected date formats, ten times the volume, the same file twice. Write a test for each.” |
State rules that must always hold | Wrong answers you couldn’t have predicted, on inputs you never imagined. | “Add property-based tests for these rules: totals reconcile to source; output rows never exceed input rows; no duplicate IDs; running twice gives identical output; these columns are never empty.” |
Audit the test list | Requirements that would have shipped with nothing verifying them. | “Name every test as a plain-English sentence. Then give me a table: each requirement in my spec, the tests that verify it, and any requirement with no test at all.” |
Ban the cheats | Suites that are technically green and completely hollow. | “List every test that is skipped, marked expected-to-fail, asserts nothing, or replaces the component it’s testing with a fake. Give me the count of real assertions per test.” |
Test the interface as a user | Screens that break invisibly, and tests that pass against a faked back end. | “Set up Playwright. Find elements by their visible labels, never internal IDs. Add screenshot comparisons on the key screens. At least one test must run the complete journey against the real running system — no faked responses in that path.” |
Make it run without you | Discipline that decays the moment you get busy. | “Run the full suite automatically on every change, and block the change if anything fails. Show me where to see the results.” |
Demand evidence, not assurance | Success that was described rather than observed. | “Don’t tell me it works. Run it and paste the actual output — tests passed, failed and skipped — then run it on the real sample file and show me the first ten rows.” |
Four principles to live by
Watch the tests fail before you trust them. A test that has never been red might not be connected to anything. Red, then green, is proof it’s wired to the real thing which is why the first countermeasure insists on seeing the failure.
Coverage and mutation score are not the same number. Coverage tells you which lines the tests ran. It says nothing about whether anything was checked. A suite with 100% coverage and no real assertions passes with flying colors. Mutation score tells you what proportion of deliberately introduced bugs the tests caught. Ask for both; trust the second.
Record interface tests yourself. Playwright’s recorder (npx playwright codegen) opens a browser, watches you click through a task, and writes the test. You perform it; the agent only transcribes. For every flow you record, the confirmation-bias problem disappears entirely. Its trace viewer then lets you scrub through a failed run like video and watch the failure happen.
Never let the agent change a test to make it pass.
When a test fails, exactly one of two things is true: the code is wrong, or the test was wrong. Which one is your decision, not the agent’s. The same goes for stored comparison images and saved expected outputs: the agent must never quietly regenerate one. Make it show you the before and after and explain what changed.
4. Push back
Agents drift toward agreement. That’s not a design choice, it’s a side effect of how they’re trained: responses that validate the user tend to get rated more highly, and the model learns accordingly. The consequence is that an agent will often go along with a flawed premise rather than challenge it.
This costs you more than it costs a developer, because you can’t independently detect the moment it becomes confidently wrong. So, you have to generate the friction on purpose. None of the prompts below require technical vocabulary.
What it does | Why it matters | What you say |
Makes the error disappear | Silences the failure instead of fixing it — disables the check, catches and ignores the error, or loosens the test. The problem is still there, just quiet. | “Don’t make the error go away. Explain why it’s happening in plain English, then propose a fix. Don’t write code yet.” |
Agrees with you instantly | You can’t independently tell when your idea was bad, so agreement feels like confirmation when it’s just compliance. | “You agreed with that immediately. Was it actually a good idea? What would you have done differently if I hadn’t suggested it?” |
Builds more than you asked | Extra features, settings and abstractions you didn’t request are extra surface area you can’t review and don’t need. | “Remove anything I didn’t ask for. Build only what’s in the spec — nothing more.” |
Invents packages | Agents sometimes cite libraries that don’t exist. Attackers register those invented names and publish malware under them. | “List every external package you added. For each one: confirm it actually exists, that it’s actively maintained, and why it’s needed.” |
Hides behind jargon | Technical vocabulary can make a weak answer sound authoritative. It’s the easiest way for you to be talked past. | “Explain that again with no technical terms, as if to a smart colleague in another department.” |
Fills in your gaps silently | Anything you didn’t specify, it decided — date formats, rounding, what counts as a duplicate. Those decisions are invisible until they’re wrong. | “What did you assume that I didn’t tell you? List every assumption you made and flag the ones I should confirm.” |
The jargon row is worth taking seriously as a general rule. If something can’t be explained to you in plain language, one of two things is true: the explanation is weak, or the thing is too complex for you to own. Both are worth knowing before it ships.
5. When Things Break: Debugging as a Non-Developer
The instinct to panic vs. the process to follow
An error message feels like proof you broke something you don’t understand and can’t fix. It isn’t. It’s a diagnostic, the system telling you exactly where it got stuck, in more precise detail than “it’s broken.” Treat the error as the start of the conversation, not the end of the project.
Describe what happened, not just that it’s broken
“It’s broken” gives an agent nothing to work with, and it will either guess or ask you the same three questions you could have answered up front. The fix is the same discipline as your acceptance examples: be concrete. What did you do, what did you expect, and what happened instead?
“I clicked ‘Generate report.’ I expected a PDF with last month’s numbers. Instead I got [describe exactly what appeared: a blank page, an error message, a report with the wrong month]. Here’s the exact error text.”
That last part matters: paste the actual error message rather than paraphrasing it. Paraphrasing is where information the agent needed quietly gets lost.
Reading error messages without understanding them
You don’t need to parse an error message to get value out of one. Most contain the same handful of ingredients regardless of the language or tool: a location (where in the process it happened), a category (what kind of thing went wrong, even if the wording is technical), and often a short human-readable line summarizing the failure, usually at the very top or the very bottom.
You don’t need to identify which part is which. You need to know that structure exists, so instead of copy-pasting fifty lines at the agent and hoping, you can ask it to do the sorting for you:
“Here’s the full error message. Tell me, in plain English: what actually went wrong, where it happened, and whether this is about my data, my setup, or something in the code. Don’t fix anything yet, just explain it back to me first.”
Asking for the explanation before the fix does two things. It gives you a chance to catch a wrong diagnosis before the agent builds on top of it, and it slowly builds your own intuition for what these messages are telling you, without requiring you to study them like a second language.
6. Work reversibly
Most of the fear around building your own tools is fear of breaking something irreversible. Remove that and you can experiment freely, which is where the value actually is.
Never let an agent work on your only copy. Duplicate the data first, every time. An agent asked to clean a file will clean the file it was given.
Don’t point it at live systems while you’re iterating. Work on a copy, a sample, or a test environment until the thing is finished and reviewed.
Get version control on day one. You don’t need fluency — you need one command that gets you back to the last state that worked.
“Set up version control for this project and commit after every change that works. Then teach me the one command that takes me back to the last working version, and tell me how to see what changed since then.”
A project you can always retreat from is a project you can be bold in. That trade is worth ten minutes of setup.
7. Know when to hand off — and plan for the afterlife
Being clear about the limit is what makes the rest of this responsible. Cross any of these lines and a developer reviews the work before it goes anywhere real:
It touches client data or personal information
It handles passwords, API keys or access tokens
It writes to a production system, a shared drive, or anything that is a source of truth for other people
It sends something outward — email, messages, or calls into a client’s systems
It runs on a schedule, unattended, with nobody watching the output
Someone other than you will start depending on it
Money moves, or the numbers reach a client, an auditor or a regulator
Everything else — which is most things — you should build. Analysis on a copy of the data, internal tooling, prototypes, anything that saves you an afternoon a week. The tripwires exist so that the space inside them can be genuinely free.
Then there’s the quieter risk. Tools built this way have a habit of becoming load-bearing by accident: useful, then relied upon, then critical, then broken on a Tuesday morning with nobody able to fix it. Decide up front which kind of thing you’ve made. If it’s disposable, say so and let it die. If people are going to depend on it, it needs a named owner who can read the code, and it needs documentation — which you should get written now, while everything is still in context.
“Write a README for this: what it does, where it runs, what it depends on, how someone would tell it’s broken, what to check first, and what it should never be used for. Write it for a develope r who has never seen this project.”
The division of labor
You are not a worse developer. You’re a different role in the same system, and it has real content: you supply the domain truth, the acceptance criteria, and the judgment about what “correct” even means for this client. The agent supplies syntax. That division works — as long as you hold up your side.
Specify before you generate. Understand the system, not the syntax. Verify behavior relentlessly, and never accept a test suite you haven’t seen fail. Argue with the machine. Keep a way back. Know where your limit is.
And never confuse “it ran” with “it’s right.”

Comments