• How To Preserve Software Decisions As Your Project Grows

    As a software project grows, one of the easiest things to lose is not code.

    It is why the code became this way.

    You may remember today that:

    • User information should remain on the device.
    • A particular dependency was deliberately rejected.
    • One screen behaves differently because of an earlier technical problem.
    • A feature was intentionally left out of the first version.
    • Two similar pieces of code were kept separate for a reason.

    Months later, those decisions may no longer be obvious.

    AI may suggest reversing them. A collaborator may assume they were accidental. Even you may forget why you made them.

    The solution is not to preserve every conversation.

    It is to preserve the project knowledge that future work will depend on.

    Code Shows What Exists, Not Always Why

    Imagine the errands app stores all tasks locally on the device.

    Someone inspecting the code can see that local storage is being used.

    But the code may not explain why.

    Perhaps the decision was:

    “The first version should work without accounts or a remote server. Personal task information should remain on the user’s device.”

    That reasoning matters.

    Without it, a future AI conversation might suggest moving tasks to an online database because it appears more convenient for a new feature.

    Technically, that may work.

    But it could contradict a deliberate product decision.

    This reveals an important distinction:

    The code records implementation. Project knowledge records intent and reasoning.

    You need both.

    Not Every Past Conversation Is Project Knowledge

    When building with AI, it is easy to accumulate enormous conversation histories.

    Most of them should not become permanent documentation.

    A conversation might contain:

    • Ideas you rejected.
    • Incorrect assumptions.
    • Temporary debugging experiments.
    • Five possible solutions before you chose one.
    • Questions that have since been answered.
    • Old descriptions of features that later changed.

    Saving all of that as permanent context can make the project harder to understand.

    The useful question is not:

    “What did we discuss?”

    It is:

    “What does someone working on this project later need to know?”

    That usually produces a much smaller amount of information.

    Preserve Decisions That Constrain Future Work

    Some decisions have little effect beyond the moment they are made.

    Others influence many future changes.

    Those are the ones worth preserving.

    For example:

    “Tasks are stored locally” is important because future features involving storage need to respect it.

    “We support iOS 18 and later” matters because dependency and API choices may depend on it.

    “The first version does not require user accounts” affects authentication, synchronisation, storage and onboarding.

    “We use the platform’s existing chart capability instead of another dependency” may prevent future AI sessions from introducing a package that was deliberately avoided.

    A useful rule is:

    Record decisions when forgetting them could cause future work to move in the wrong direction.

    You do not need to record every button position or variable name.

    Those are normally visible from the project itself.

    Preserve The Reason, Not Just The Decision

    A list of rules without reasoning can become dangerous.

    Imagine your project notes say:

    “Do not use cloud storage.”

    Six months later, your requirements change and synchronisation between devices becomes essential.

    Is cloud storage forbidden because of:

    • Privacy?
    • Cost?
    • Technical complexity?
    • The scope of the first version?
    • A temporary limitation?

    Without the reason, you cannot tell whether the old decision still applies.

    A better record would be:

    Decision: Keep task data on the device for the first version.
    Reason: Accounts and synchronisation are outside the first release, and local storage keeps the product simpler while protecting the intended private experience.
    Revisit when: Cross-device synchronisation becomes a confirmed requirement.

    Now the decision can evolve intelligently.

    The reason tells you what problem the decision was solving.

    Distinguish Decisions From Current Project State

    Projects contain at least two different kinds of useful knowledge.

    Current state

    This describes what the project is like now.

    For example:

    “Users can create, edit, complete and delete tasks.”

    Or:

    “The app currently stores tasks locally.”

    Decision history

    This explains why something important was chosen.

    For example:

    “We chose local storage because the first version should not require an account.”

    The distinction matters because current state can change frequently.

    Decision reasoning usually changes less often.

    You do not want an old project note claiming:

    “Task editing is not supported” after task editing has already been built.

    That is stale current-state information.

    At the same time, an older decision such as:

    “Keep the first version account-free” may still be completely valid.

    Good project knowledge makes it clear which kind of information you are reading.

    Establish A Source Of Truth

    As projects grow, the same fact can appear in many places.

    Perhaps your storage decision exists in:

    • An old AI conversation.
    • A planning document.
    • A comment inside the code.
    • A task list.
    • A newer conversation saying something different.

    Which one wins?

    Without an answer, conflicting project knowledge begins to accumulate.

    A source of truth is the place you treat as the current authoritative version of important information.

    For a small project, this does not need to be complicated.

    You might keep a short project document containing:

    • The product’s current purpose.
    • Important technical decisions.
    • Important constraints.
    • Major decisions that future work must respect.
    • Known decisions that are intentionally still unresolved.

    The exact format matters less than consistency.

    When an important decision changes, update the source of truth.

    Do not expect future AI sessions to reconstruct the truth from dozens of old conversations.

    Mark Old Decisions As Replaced

    Changing your mind is normal.

    The danger is leaving both the old and new decisions looking equally valid.

    Suppose the project originally says:

    “The app will never require accounts.”

    Later, user research convinces you that cross-device synchronisation is essential.

    The new decision might become:

    “Accounts will be optional and required only for synchronisation.”

    Do not simply add that underneath the old rule.

    Now the project contains a contradiction.

    Instead, make the relationship explicit:

    Previous decision: No user accounts.
    Status: Replaced.
    Current decision: Accounts are optional and used for synchronisation.
    Reason for change: Cross-device access became a confirmed product requirement.

    This creates a useful history without making the project ambiguous.

    A project should be able to change its mind without losing track of what is currently true.

    Preserve Discoveries That Are Expensive To Rediscover

    Not all valuable project knowledge begins as a deliberate design decision.

    Sometimes you learn something through failure.

    Imagine you spend an hour discovering that a particular platform behaviour causes tasks to be duplicated when information is loaded in a certain way.

    You eventually find a reliable solution.

    The code contains the fix.

    But six months later, AI may see the implementation and suggest simplifying it back to the original broken approach.

    That lesson was expensive.

    Preserve it.

    For example:

    “Task loading deliberately happens here rather than when the screen first appears. Loading on every appearance previously caused duplicate requests.”

    This is different from documenting every bug ever fixed.

    The useful knowledge is the part that prevents someone from repeating a non-obvious mistake.

    A good rule is:

    If the reason behind unusual code would not be obvious to a competent person reading it later, preserve the reason somewhere appropriate.

    Do Not Turn Documentation Into A Second Codebase

    There is a danger in documenting too much.

    Suppose you maintain a detailed document describing every screen, property, function and file.

    Now every code change also requires a matching documentation change.

    Soon the document becomes outdated.

    At that point it becomes worse than having no document because it confidently tells you things that are no longer true.

    The code itself should remain the main source for details that can be discovered easily by inspecting the project.

    Project knowledge should concentrate on information the code cannot explain reliably:

    • Intent.
    • Trade-offs.
    • Constraints.
    • Non-obvious reasoning.
    • Important rejected alternatives.
    • Decisions that affect future work.

    Document the knowledge, not a duplicate description of the entire codebase.

    Some Decisions Belong Close To The Code

    Not every explanation belongs in a central project document.

    Suppose one particular function contains unusual logic because of a platform-specific behaviour.

    The reason may be most useful directly beside that code.

    A concise comment might explain:

    “Keep this operation after the save completes; moving it earlier can display stale task data.” That information is highly local.

    Someone reading the relevant code needs it there.

    By contrast:

    “The app does not require accounts in the first version”, affects the whole project. That belongs in broader project knowledge.

    The useful question is:

    “Who needs this information, and when will they need it?”

    Put the knowledge where it is most likely to be encountered at the moment it matters.

    Give AI The Current Decisions, Not The Entire History

    Once important knowledge has been preserved properly, working with AI becomes easier.

    Instead of saying:

    “Read these twelve previous conversations and work out what we decided.” You can provide the current project knowledge.

    For example:

    “Before making changes, follow the current project decisions document. If the existing code appears to conflict with it, point out the conflict instead of silently choosing one.”

    That is much stronger than relying on conversational memory.

    Old conversations can still be useful when investigating how a decision was reached.

    They should not need to function as the project’s primary memory.

    Let The Project Become More Independent Of The Conversation

    This is an important transition for a vibe coder.

    At the beginning, the project may live largely inside a conversation:

    You ask.
    AI answers.
    You make changes.
    You remember what happened.

    That works while the project is small.

    Eventually, the project should carry more of its own knowledge.

    A new AI session—or a new human collaborator—should be able to understand its important decisions without requiring you to retell the entire story.

    That is a sign that the project is becoming more durable.

    The conversation helped create the software.

    The software should not permanently depend on remembering the conversation that created it.

    Preserve Knowledge That Changes Future Decisions

    The purpose of project memory is not historical completeness.

    It is future usefulness.

    Preserve:

    • Important product decisions.
    • Important technical choices.
    • Constraints that future work must respect.
    • Reasons behind non-obvious implementations.
    • Expensive lessons you do not want repeated.
    • Decisions that have been replaced and what replaced them.

    Avoid filling it with information that is temporary, obvious from the code, or no longer relevant.

    That completes Chapter 4.

    You now have a basic process for directing AI effectively: give it the relevant context, define the change clearly, structure large work intelligently, and preserve the knowledge that should survive beyond one conversation.

    Chapter 5 moves from directing AI to keeping control of the project itself.

    In Blog 15, we will look at how to save working versions and make changes reversible, including why code history and backups solve different problems.

  • How To Break Large Coding Features Into Manageable Steps

    A large software feature is rarely one piece of work.

    It is usually a collection of smaller changes that depend on one another.

    The important skill is not simply making the tasks smaller. It is discovering what depends on what, then choosing an order that lets you learn whether the approach works before too much is built on top of it.

    This is especially useful when working with AI. AI can produce a large amount of code quickly, but speed does not remove dependencies between parts of a system.

    Before asking AI to build a large feature, first understand its shape.

    Find What Must Exist First

    Suppose our errands app eventually allows people to share a task list with someone else.

    “Add shared lists” sounds like one feature.

    But other capabilities may be hiding inside it:

    • The app must know which list is being shared.
    • It must know which people can access that list.
    • Shared information needs somewhere both people can reach.
    • Someone needs a way to invite another person.
    • The invited person needs a way to access the shared list.
    • Changes made by one person need to become available to the other.

    These pieces are connected.

    You cannot meaningfully build the invitation experience if the app has no concept of who can access a list.

    And there is little value polishing the sharing interface if you have not established that information can actually be shared between two people.

    This relationship is a dependency between pieces of work.

    One step is a prerequisite for another.

    A useful question is:

    “What must already work before this part can work?”

    Keep asking that question and the internal structure of the feature begins to appear.

    Think Of The Work As A Dependency Map

    A simple to-do list suggests that every task is just the next item in a sequence.

    Software work is often more like a map.

    For shared lists, part of that map might look roughly like this:

    Shared information
    ↓
    List membership
    ↓
    Invitation
    ↓
    Member management

    But another part of the work might be relatively independent.

    For example, the visual design of a small “Shared” indicator may not need to wait for every member-management feature to exist.

    Thinking this way helps distinguish three kinds of work:

    Prerequisite work must happen before something else can function.

    Dependent work relies on an earlier capability.

    Independent work can be developed without waiting for another branch of the feature.

    You do not need specialist planning software to do this.

    Even a few arrows on paper can reveal that what looked like one large task is actually a set of connected changes.

    Do Not Automatically Build Entire Layers At Once

    There are different ways to divide a feature.

    One approach is to complete an entire technical layer before moving to the next:

    Build all the storage → build all the application logic → build all the interface.

    Sometimes that order is necessary.

    But it can also mean doing a large amount of work before you see whether the pieces actually cooperate.

    Another approach is to build a small path through the whole system.

    For example, instead of building every possible part of shared lists, you might first prove one simple journey:

    Person A shares one list → Person B opens it → Person B can see one task.

    That narrow path might involve a small amount of interface, logic and storage.

    It does not complete the feature.

    But it proves that the main parts can work together.

    This kind of end-to-end piece is often called a vertical slice.

    The name is less important than the idea:

    Build the smallest meaningful path through the system that proves something important.

    Once that path works, you can expand it.

    Perhaps the next slice allows Person B to add a task.

    Another might allow the owner to remove someone from the list.

    You are growing a working capability rather than building several large disconnected layers and hoping they join correctly later.

    Build The Uncertain Part Before The Polished Part

    Not every part of a feature carries the same uncertainty.

    Imagine you are unsure whether your chosen approach can reliably keep a shared list available to two people.

    At the same time, you already know how to build a polished invitation screen.

    It may be tempting to start with the interface because it is visible and straightforward.

    But the uncertain part is more important.

    If the underlying sharing approach turns out to be unsuitable, much of the polished interface may need to change.

    A stronger sequence would prove the difficult assumption first:

    Can two test users reliably access the same information?

    You might initially use an ugly temporary interface to find out.

    If the answer is yes, you have learned something valuable.

    Now investing in the full experience makes more sense.

    If the answer is no, you have discovered the problem before building several other pieces on top of it.

    This leads to an important principle:

    When possible, test an important uncertainty before building heavily on the assumption that it will work.

    The riskiest part does not always need to be the first code you write. It may have prerequisites of its own.

    But once those prerequisites exist, important uncertainty should not be buried at the end of the plan.

    A Working Slice Can Teach You More Than A Detailed Plan

    You can plan a feature carefully and still discover things only when the software begins working.

    Suppose the first simple version of shared lists works technically.

    But when you use it, you realise that people cannot easily tell whether a list is private or shared.

    That reveals a requirement that may not have seemed important on paper.

    Or perhaps sharing one list works well, but supporting other shared lists exposes confusion about ownership.

    The working software has taught you something.

    This is why useful implementation plans leave room for learning.

    You do not necessarily need to design every later step before starting the first one.

    Plan enough to understand:

    • The main dependencies.
    • The first useful path.
    • The important uncertainty you want to resolve.

    Then use what you learn to improve the remaining sequence.

    Some Changes Should Stay Together

    Breaking work apart has limits.

    Suppose one change would leave the project unable to run until the immediately following change is also completed.

    Those pieces may belong together.

    For example, imagine changing how the app represents a task in a way that requires the code loading those tasks to change at the same time.

    Doing only half of that work may create a temporarily broken project without providing any useful checkpoint.

    The aim is therefore not:

    “Make every step as small as physically possible.”

    It is:

    Make every step as small as practical, while still leaving a coherent result.

    A good stopping point should give you something understandable.

    You should be able to say:

    “This capability now works.”

    or:

    “This foundation now exists and the existing app still works with it.”

    If you cannot explain what the intermediate state means, the division may not be useful.

    Use Dependencies To Choose The Order

    Once you can see the pieces, ordering becomes easier.

    Consider a simplified shared-list feature:

    A. Store information somewhere multiple people can access.
    B. Represent who belongs to a list.
    C. Allow one test user to see a list belonging to another.
    D. Build the invitation experience.
    E. Allow members to add and complete tasks.
    F. Add controls for removing members.

    Some ordering is forced by dependency.

    You cannot remove a member before membership exists.

    Other ordering is a strategic choice.

    This is a much stronger way to plan than simply asking:

    “What screen should we build first?”

    The better question is:

    “What needs to be true first, and what should we prove before more work depends on it?”

    Ask AI For A Dependency-Aware Plan

    For a genuinely large feature, AI can help you find the structure before it starts writing code.

    You might say:

    “Do not implement this yet. Break the feature into coherent pieces. Show which pieces depend on others, identify the smallest useful end-to-end version we could build first, and point out any important assumption we should prove early.”

    This is different from asking AI for a generic checklist.

    You are asking it to explain the relationships between the work.

    You can then examine the proposed order.

    Perhaps AI has placed a polished interface before an uncertain technical capability.

    Perhaps two supposedly separate steps actually need to happen together.

    Perhaps the first version is still much larger than necessary.

    Planning before generation gives you the opportunity to change the sequence while changing it is still cheap.

    The Goal Is Earlier Evidence

    Breaking large changes into smaller parts is not valuable merely because smaller tasks look more organised.

    The real advantage is that each useful step can tell you something.

    You learn:

    Does the underlying approach work?

    Then:

    Do the connected parts work together?

    Then:

    Does the next behaviour work on top of them?

    Instead of making one enormous bet, you make a sequence of smaller bets and learn between them.

    That is a powerful way to build software with AI.

    AI can accelerate each implementation step.

    Your responsibility is to make sure those steps are being taken in an intelligent order.

    For a large change, remember three questions:

    What depends on what?

    What is the smallest end-to-end path that would prove the idea works?

    Which important uncertainty should we resolve before building more on top of it?

    Those questions turn a large feature from a vague block of work into an understandable sequence.

    As those sequences grow across multiple conversations and development sessions, another problem appears: important decisions and discoveries can be forgotten.

    In Blog 14, we will look at how to preserve project knowledge so earlier decisions continue to guide the project as it evolves.

  • How To Ask AI To Change Your Code Clearly

    A good AI coding request makes the intended change clear without pretending you already know every implementation detail.

    You should be able to explain:

    • What needs to change.
    • What should happen afterward.
    • What should remain unchanged.
    • Where the change begins and ends.
    • Which important questions are still unresolved.

    This is different from simply giving AI more context.

    In the previous blog, we looked at helping AI understand the project it is working on.

    Now the task is to turn an intention such as:

    “Make task editing better.” Into something that can actually be implemented and checked.

    The goal is not to write an enormous prompt.

    It is to remove the ambiguity that could materially change the result.

    Start With The Change, Not The Implementation

    When people work with AI, they often jump straight to a technical solution.

    For example:

    “Add another state variable and open a modal when the button is pressed.”

    That may be the right implementation.

    But perhaps your real intention is simply:

    “When a user taps Edit on a task, they should be able to change its title and save the updated task.”

    The second version is often a better starting point because it tells AI what outcome is required without unnecessarily deciding how the code must achieve it.

    This matters because your suggested implementation may be:

    • More complicated than necessary.
    • Inconsistent with the existing project.
    • Based on an incorrect assumption.
    • Solving the symptom rather than the real problem.

    AI should not be allowed to invent the product requirement.

    But it can often help determine the implementation.

    A strong request therefore begins with:

    What should become true after this change?

    Describe One Concrete Change

    Requests become difficult when different intentions are bundled together.

    Imagine you say:

    “Improve the task screen, add editing, make it look cleaner, fix the sorting, add recurring tasks, and make it faster.”

    There may be six different pieces of work inside that sentence.

    Each one could affect different parts of the app.

    If AI attempts all of them together, several problems appear.

    It becomes harder to understand what changed.

    It becomes harder to identify which change caused a problem.

    It becomes harder to check whether each behaviour is correct.

    And AI has more opportunities to make assumptions about how the pieces should interact.

    A clearer request might begin with:

    “Add the ability to edit the title of an existing task.”

    That gives the work a definite centre.

    This does not mean every request must produce only one tiny code change.

    A coherent feature may naturally require some files or behaviours.

    The important distinction is whether those changes serve one understandable outcome.

    Blog 13 will go further into breaking large pieces of work into smaller coherent parts.

    For now, start by making the intended change identifiable.

    Say What The User Should Be Able To Do

    A feature request becomes clearer when it describes observable behaviour.

    Compare:

    “Implement task editing.”

    With:

    “From the task details screen, the user can choose Edit, change the task title, save it, and then see the updated title.”

    The second version gives you something you can eventually check.

    It describes behaviour from the user’s perspective rather than merely naming a feature.

    Useful requests often answer questions such as:

    • Where does the interaction begin?
    • What can the user do?
    • What should happen when they complete the action?
    • What should they see afterward?

    This connects directly to Blog 6, where we defined correct behaviour through requirements and examples.

    You do not need to repeat the entire specification every time.

    But your request should contain enough observable behaviour to distinguish a correct implementation from one that merely looks plausible.

    Include The Important Rules

    Features usually have rules.

    Suppose the user edits a task title.

    Can the title be empty?

    Can it contain only spaces?

    Is there a maximum length?

    Can a completed task still be edited?

    What happens if the user changes the title and then cancels?

    If those questions already have answers, include the ones that matter.

    For example:

    “The title cannot be empty. If the user cancels, the existing title must remain unchanged.”

    Those two sentences remove other possible interpretations.

    You do not need to list every imaginable case.

    Focus on rules that could change what AI builds.

    A useful test is:

    “Could two reasonable developers implement this request differently because I have not stated this rule?”

    If the answer is yes and the difference matters, clarify it.

    Define The Boundary Of The Change

    One of the most useful things you can tell AI is where the task stops.

    Suppose you want to add editing to task titles.

    AI might notice that the task model also contains a date, notes, category, priority, and completion state.

    Should all of those become editable too?

    If you do not say, AI has to decide.

    A clear boundary might be:

    “For this change, only the task title should be editable. Do not add editing for the date, notes, category, or completion state.”

    The word boundary is useful here.

    A boundary tells AI:

    • Which behaviour belongs to this task.
    • Which nearby behaviour does not.
    • Which files or systems should not be expanded unnecessarily.
    • Which improvements can wait.

    This is especially important because AI often sees additional opportunities while working.

    Some of those suggestions may be good.

    But a good idea discovered during one change does not automatically belong inside that change.

    Keeping boundaries clear protects the project from accidental expansion.

    Say What Must Stay The Same

    A change rarely exists in isolation.

    Suppose task titles can already be created, displayed, saved, and restored after the app closes.

    You now want to make them editable.

    A useful request might include:

    “Existing task creation, completion, deletion, and persistence should continue to behave as they do now.”

    This creates a preservation requirement.

    You are not asking AI to redesign the entire task system.

    You are changing one capability inside it.

    This matters because AI can sometimes produce a clean-looking solution by replacing more existing code than necessary.

    A request that states what must remain intact reduces that risk.

    The general pattern is:

    Change this. Preserve that.

    That simple distinction can make a request dramatically clearer.

    Distinguish Behaviour From Appearance

    A request can involve what the software does, what it looks like, or both.

    These should be made explicit.

    Suppose you say:

    “Make the task editing screen better.”

    Does “better” mean:

    • Easier to understand?
    • Visually cleaner?
    • Faster?
    • Fewer taps?
    • Better validation?
    • More consistent with another screen?

    The word describes your judgement, not the desired result.

    Instead, separate the concerns.

    For example:

    “The editing behaviour already works. Change only the layout so the title field and Save button follow the same visual structure as the task creation screen.”

    Or:

    “Keep the current design. Change only the behaviour so “Save” is disabled when the title is empty.”

    Those are very different tasks.

    Clear requests tell AI whether the problem is behavioural, visual, structural, or some combination of them.

    Use Examples When Words Leave Room For Interpretation

    Examples are often the fastest way to make a requirement precise.

    Suppose your app groups tasks under headings.

    You could say:

    “Group tasks appropriately by date.”

    But “appropriately” requires interpretation.

    An example is clearer:

    “A task due today appears under Today. A task due tomorrow appears under Tomorrow. Later tasks appear under Upcoming.”

    Examples are especially useful for:

    • Sorting.
    • Formatting.
    • Calculations.
    • Validation.
    • State changes.
    • Conditional behaviour.
    • Edge cases.

    They are not a replacement for the general rule.

    They help reveal what the rule means.

    For example:

    “Sort tasks by due date from earliest to latest. For example, a task due on Monday should appear above one due on Wednesday.”

    Now both the rule and an example are visible.

    Do Not Hide Uncertainty

    Sometimes you know what problem you want solved but have not decided exactly how the experience should work.

    That is not a reason to let AI quietly choose.

    Suppose you want users to edit a task, but you have not decided whether editing should happen:

    • On a separate screen.
    • Directly inside the task list.
    • In a small overlay.
    • On the existing details screen.

    You could ask:

    “Add task editing.”

    AI will choose one.

    But that means a product decision has been made without you noticing.

    A better request is:

    “I want users to be able to edit an existing task, but I have not decided whether editing should happen on the details screen or in a separate editing screen. Before implementing it, compare those two approaches for this project.”

    This is an important habit.

    Unresolved decisions should be made visible rather than filled silently with assumptions.

    Not every uncertainty must stop the work.

    Some implementation details can safely be left to AI.

    The important question is whether the uncertainty affects the product, architecture, data, security, or another meaningful project decision.

    If it does, resolve it deliberately.

    Separate Decisions You Own From Decisions AI Can Make

    You do not need to specify everything.

    That would defeat much of the value of using AI.

    The useful distinction is between decisions that affect what you are building and decisions about the mechanics of implementing it.

    You might decide:

    “Editing must happen from the existing task details screen.”

    But allow AI to decide:

    • Which helper function should handle the update.
    • How to organise small pieces of internal logic.
    • Which existing project pattern should be reused.

    You remain responsible for the important result and constraints.

    AI can handle implementation decisions within those boundaries.

    This balance avoids two extremes.

    In one extreme, you tell AI almost nothing and let it design the product for you.

    In the other, you prescribe every line of implementation even when you do not have enough technical knowledge to know whether your approach is good.

    A stronger approach is:

    Be precise about the outcome and important constraints. Be flexible about implementation details that do not materially affect the product.

    State Whether AI May Change The Existing Structure

    Sometimes a feature can be implemented cleanly within the current design.

    Sometimes the existing design makes the feature awkward.

    If AI is allowed to restructure the project whenever it sees an opportunity, a small request can turn into a large rewrite.

    If AI is never allowed to change structure, you may force increasingly awkward solutions into poor foundations.

    You can handle this explicitly.

    For a small change:

    “Use the existing structure. Do not refactor unrelated code.”

    For a larger problem:

    “Prefer the existing structure, but if it prevents a reliable implementation, explain the structural change you recommend before making it.”

    The second version is especially useful.

    It does not forbid architectural judgement.

    It prevents that judgement from becoming an invisible side effect of another task.

    Ask For Questions Before Code When Important Details Are Missing

    Sometimes the strongest instruction is not:

    “Implement this.”

    It is:

    “Tell me what you still need to know before implementing this.”

    This works particularly well when the change affects many behaviours.

    For example:

    “I want users to postpone an existing task to another day. Before making changes, identify any unresolved product or data decisions that would materially affect the implementation.”

    AI might ask:

    • Can completed tasks be postponed?
    • Should postponing preserve the original due date anywhere?
    • What happens to reminders?
    • Can the user choose any date in the past?

    Those questions expose decisions hidden inside the seemingly simple word postpone.

    You do not need AI to ask twenty minor questions before every task.

    The instruction should focus on uncertainties that could materially change the solution.

    A useful phrasing is:

    “If anything important is ambiguous, ask before assuming.”

    Be Careful With Words That Sound Clear But Are Not

    Many common instructions carry very little information.

    Examples include:

    • “Make it better.”
    • “Fix it.”
    • “Clean this up.”
    • “Make it modern.”
    • “Improve the UX.”
    • “Optimise it.”
    • “Make it scalable.”
    • “Make it professional.”
    • “Make the code cleaner.”

    These requests may express a valid intention.

    But they do not define what success looks like.

    Suppose you ask AI to:

    “Make this screen cleaner.”

    It might remove information you wanted.

    It might change colours.

    It might reduce spacing.

    It might restructure the navigation.

    All could reasonably be interpreted as “cleaner.”

    Instead, explain the observable problem.

    For example:

    “The screen currently shows five controls at once, even though three are rarely used. Keep the two main actions visible and move the three secondary actions behind the existing “More” menu.”

    Now “cleaner” has become a specific change.

    Whenever you use a vague word, ask:

    “What would I actually expect to be different when this is finished?”

    Write that instead.

    Avoid Asking For A Feature And A Diagnosis At The Same Time

    There is an important difference between:

    “Add a loading indicator.”

    and:

    “The screen freezes for three seconds, so add a loading indicator.”

    The second request assumes the solution before the cause has been understood.

    Perhaps a loading indicator is appropriate.

    Perhaps the freeze is a bug that should be fixed instead.

    When the underlying problem is uncertain, separate investigation from implementation.

    For example:

    “Opening this screen sometimes freezes the interface for about three seconds. Investigate the cause first. Do not add a workaround until we understand why it happens.”

    That is a clearer task because it matches your actual knowledge.

    Blog 21 will explore debugging in much greater depth.

    The principle here is simply not to disguise a guess as a requirement.

    Tell AI How You Will Recognise Completion

    One of the strongest parts of a request is a simple description of what should be true when the work is finished.

    Suppose the feature is task editing.

    Completion might mean:

    • A user can open an existing task.
    • They can change its title.
    • Saving updates that task rather than creating another one.
    • The updated title appears in the task list.
    • The change remains after closing and reopening the app.
    • Cancelling leaves the original task unchanged.

    These statements function as acceptance criteria.

    Acceptance criteria are conditions that must be satisfied for the work to count as complete.

    You do not need formal project-management language to use them.

    They can simply be a short list of observable outcomes.

    They are valuable because they create a shared definition of success between you and the AI.

    Later, they also give you something concrete to test.

    A Clear Request Can Still Be Short

    Good prompting is sometimes mistaken for long prompting.

    Length is not the goal.

    Compare this vague request:

    “Can you improve task editing and make sure everything works nicely?”

    With this one:

    “Allow users to edit the title of an existing task from the task details screen. The title cannot be empty. Saving must update the existing task and persist after the app restarts. Cancelling must leave the task unchanged. Do not add editing for any other task fields or change existing task creation, completion, or deletion behaviour.”

    The second request is not particularly long.

    It is simply specific where specificity matters.

    A strong request tends to contain information rather than decoration.

    A Useful Structure For AI Coding Requests

    You do not need to use a rigid template for every request.

    But when a change is substantial, it can help to think through five parts.

    1. The change

    What should become different?

    “Allow users to edit the title of an existing task.”

    2. The expected behaviour

    What should the user observe?

    “The updated title should appear throughout the app and remain after the app is reopened.”

    3. The rules

    What conditions govern the behaviour?

    “The title cannot be empty.”

    4. The boundaries

    What is outside this piece of work?

    “Do not add editing for any other fields.”

    5. The unresolved questions

    What important decisions have not yet been made?

    “If the current details screen cannot support editing cleanly, explain the alternatives before changing the navigation.”

    Together, those five pieces turn an intention into actionable work.

    Compare A Weak Request With A Stronger One

    Imagine the errands app already allows users to create, complete, and delete tasks.

    You now want editing.

    A weak request might be:

    “Add editing to tasks.”

    AI has to decide:

    • Which fields are editable.
    • Where editing happens.
    • What validation exists.
    • What Save does.
    • Whether Cancel exists.
    • Whether other task behaviour should change.
    • Whether the storage system needs replacing.
    • What happens to existing tasks.

    A stronger request might be:

    “Add editing for existing task titles. Editing should begin from the current task details screen. The user can change the title and either Save or Cancel. Titles cannot be empty. Save updates the same task and the change must persist after the app closes. Cancel leaves the original title unchanged. Do not make the date, completion state, or other task fields editable. Keep the existing task storage system and existing create, complete, and delete behaviour. If the current structure makes this unreliable, explain why before making a broader structural change.”

    That request gives AI considerable freedom to write the code.

    But it does not give AI freedom to redefine the feature.

    That is the balance you want.

    Do Not Treat The First Request As A Contract With Reality

    Even a well-written request may reveal new information once AI inspects the code.

    Perhaps the task details screen does not actually own the information you thought it did.

    Perhaps changing the title also affects another part of the app.

    Perhaps an earlier implementation choice makes the requested behaviour unsafe.

    A clear request does not mean:

    “Do exactly this even if new evidence shows the plan is wrong.”

    It means:

    “This is the result and boundary I currently intend. Tell me if the existing project creates an important conflict.”

    Software work is iterative.

    The purpose of clarity is not rigidity.

    It is to make deliberate changes possible.

    Good Requests Reduce Accidental Decisions

    When an AI coding request is vague, implementation still happens.

    The missing decisions do not disappear.

    Something—makes them.

    If you do not specify whether Cancel should discard changes, AI may decide.

    If you do not specify whether existing information must remain compatible, AI may decide.

    If you do not define the boundary of the feature, AI may decide how far it should expand.

    Some of those decisions are harmless implementation details.

    Others belong to you.

    The skill is learning the difference.

    Before sending a meaningful coding request, ask:

    What am I asking to change?

    What should be true afterward?

    What must remain true?

    Where does this task stop?

    What important question am I still leaving unanswered?

    If those answers are clear, AI has much less room to solve a different problem from the one you intended.

    But even a clear request can become too large to implement safely in one move.

    In Blog 13, we will look at how to break large software changes into smaller, coherent parts so each one can be understood, implemented, and checked before moving on.

  • How To Give AI Enough Context To Work On Your Code

    AI writes better code when it understands the project around the code.

    A request such as:

    “Add editing to my tasks” may sound clear to you because you already know what a task is, where tasks are stored, how the app currently behaves, which screen displays them, and what you do not want changed.

    The AI may know none of that.

    It can still produce code. But when important context is missing, it has to fill the gaps with assumptions.

    Sometimes those assumptions happen to match your project.

    Sometimes they create the wrong behaviour, duplicate something that already exists, change an important decision, or solve the right problem in the wrong part of the app.

    The goal is therefore not to give AI as much information as possible.

    It is to give AI the information that changes how the work should be done.

    Context And Instructions Are Different

    Before going further, it helps to separate two ideas.

    Context describes the situation.

    Instructions describe what you want done.

    For example:

    “Tasks are stored locally and remain available after the app closes” – is context.

    “Allow the user to edit an existing task” – is an instruction.

    Both matter, but they serve different purposes.

    If AI receives the instruction without the context, it might introduce a completely different way of storing edited tasks.

    If it receives the context but no clear instruction, it understands the project but does not know what you want changed.

    This blog focuses on the first half: giving AI an accurate picture of the situation.

    Blog 12 will focus on turning your intention into a clear request.

    AI Does Not Automatically Share Your Mental Model

    As you work on an app, you gradually build a mental model of it.

    You remember things such as:

    • Why a feature exists.
    • Which behaviour was intentional.
    • Where information is stored.
    • What has already been tried.
    • Which parts of the app should remain unchanged.
    • Why you rejected an earlier approach.
    • What users are supposed to experience.

    That knowledge may feel obvious because you have been living with the project.

    AI only knows the parts available in its current context.

    Depending on how you are working, it may have access to some project files, previous conversation messages, documentation, or other information.

    But you should not assume it knows something simply because you know it.

    This creates a useful habit:

    Before asking AI to change the project, ask yourself what facts you are relying on that the AI may not know.

    Those facts are often the context it needs.

    Start With The Relevant Product Context

    Code exists to produce behaviour for someone.

    That means technical context alone is not always enough.

    Suppose the errands app from earlier blogs has a home screen showing today’s tasks.

    You ask AI to add an option to hide completed tasks.

    The implementation depends partly on why the screen exists.

    If the purpose is to help users see only what still needs attention, hiding completed tasks may make sense by default.

    If the screen is supposed to give users a complete record of everything they did today, automatically hiding them may work against the intended experience.

    The code cannot answer that product question by itself.

    Useful product context might include:

    “The home screen is meant to show what the user still needs to do today. Completed tasks remain accessible in task history.”

    That one sentence changes how the feature should be approached.

    You do not need to repeat the entire product vision every time.

    Give the part that affects the current decision.

    Explain The Current Behaviour Before Changing It

    AI needs to know what already happens.

    This is particularly important when you are modifying an existing feature rather than creating something completely new.

    Imagine your app currently lets a user tap a task to mark it complete.

    Now you want tapping the task to open its details instead.

    If you simply say:

    “Make tapping a task open the details screen.”

    AI may replace the old behaviour without considering whether task completion still needs another interaction.

    A more useful description of the current situation is:

    “At the moment, tapping a task marks it complete. I now want task tapping to open its details, but users must still be able to mark tasks complete.”

    You have not yet fully specified the solution.

    You have made the existing behaviour visible.

    That helps prevent accidental loss of functionality.

    A useful pattern is:

    What happens now → what is changing → what must continue to work.

    Blog 12 will develop the change itself more precisely.

    For context, the important thing is that AI understands the starting point.

    Give AI The Relevant Code, Not Random Code

    When AI needs to modify existing software, it usually needs to see the code that controls the behaviour.

    But sending large amounts of unrelated code can be almost as unhelpful as sending too little.

    Suppose a button on your task screen is behaving incorrectly.

    The AI may need:

    • The screen containing the button.
    • The code that runs when the button is pressed.
    • The data being changed.
    • Any nearby code that determines whether the button is shown.

    It probably does not need an unrelated settings screen.

    The principle is:

    Provide enough surrounding code to understand the behaviour, not every file merely because it belongs to the project.

    This becomes easier as your understanding of the app improves.

    The previous chapter taught you to recognise interface code, application logic, storage, external services, data flow, and dependencies.

    Those ideas help you ask:

    “Which parts of the system participate in this behaviour?”

    Those are the places AI may need to inspect.

    Sometimes The Best Context Is The Project Itself

    If your AI tool can inspect the project, you do not always need to manually copy every relevant file into the conversation.

    Instead, you can direct it toward the right area.

    For example:

    “Before changing this, inspect the task list screen, the task model, and the code responsible for saving tasks. Explain how task completion currently works.”

    This is valuable because it separates understanding from changing.

    AI first builds a picture of the existing implementation.

    Only after that should it modify the project.

    This can prevent a common vibe-coding failure: asking AI to implement a feature before it understands how the current version already works.

    For significant changes, inspection is often a better first move than immediate generation.

    Tell AI About Important Constraints

    A constraint is something that limits which solutions are acceptable.

    Suppose your app stores all user information on the device because you deliberately want the first version to work without creating accounts or sending personal information to a server.

    You ask AI to add synchronisation between two screens.

    Without the storage constraint, it may suggest introducing a cloud database.

    Technically, that could solve the problem.

    Architecturally, it could contradict a major project decision.

    Relevant constraints might include:

    • The platforms the app must support.
    • Whether the app must work offline.
    • Where information is allowed to be stored.
    • Technologies already chosen for the project.
    • Whether adding another dependency is acceptable.
    • Performance or accessibility requirements.
    • Behaviour that must remain backward compatible.
    • Areas of the project that should not be changed.

    Notice how this builds directly on Blog 10.

    A dependency decision becomes context for future AI work.

    If you previously decided:

    “Do not add another charting dependency because the project already has the capability we need,” future AI work should know that before attempting to solve another chart problem.

    Otherwise, the same decision may be unknowingly reopened.

    Separate Requirements From Constraints

    These two ideas are related, but they are not identical.

    A requirement describes something the app needs to do.

    For example:

    “Users must be able to edit a task title.”

    A constraint limits how the solution can be implemented or what else may change.

    For example:

    “Keep tasks stored locally using the existing storage system.”

    That distinction helps AI reason about the solution.

    The first tells it the destination.

    The second helps define the road it is allowed to take.

    Blog 6 covered defining correct behaviour through requirements.

    Here, the lesson is that those existing requirements and constraints become useful context whenever AI works on the relevant part of the app.

    Show The Problem, Not Just Your Interpretation Of It

    When something is broken, developers often describe what they think caused the problem.

    For example:

    “The database isn’t saving properly.”

    But perhaps the database is working correctly and the screen simply fails to reload the saved information.

    If AI receives only your diagnosis, it may begin repairing the wrong thing.

    Better context includes the evidence you actually observed:

    “I create a task and it appears immediately. After closing and reopening the app, the task is gone.”

    You can also provide useful evidence such as:

    • Error messages.
    • Unexpected output.
    • Steps that consistently cause the problem.
    • Logs.
    • Screenshots where relevant.
    • The behaviour you expected.
    • The behaviour you actually saw.

    This does not mean your hypothesis is useless.

    You can say:

    “I suspect the problem is related to saving, but I am not certain.”

    That distinguishes evidence from interpretation.

    Blog 21 will cover debugging as a full investigative process.

    For now, the important habit is simple:

    Give AI the facts of the problem before asking it to accept your explanation of the problem.

    Include Relevant History When The Present Code Does Not Explain It

    Sometimes looking at the current code is not enough.

    A project may contain a strange-looking decision for a good reason.

    Imagine AI sees duplicated logic and proposes combining it.

    Normally, removing unnecessary duplication might be sensible.

    But perhaps the two flows were deliberately separated because one behaves differently on an older platform version.

    The current code may show what is different without showing why.

    That missing reasoning is project history.

    Useful historical context might include:

    “These two flows were deliberately kept separate because the older platform behaves differently.”

    Or:

    “We previously tried loading this information when the screen opens, but it caused duplicated requests, so we moved it here.”

    Without that context, AI can accidentally undo previous learning.

    This is one reason important decisions should eventually be preserved outside individual conversations.

    Blog 14 will cover that directly.

    Tell AI What Is Already Working

    When something needs improvement, it is easy to focus only on the broken or missing part.

    But the working behaviour may be equally important context.

    Suppose the task creation screen correctly:

    • Creates tasks.
    • Saves their date.
    • Stores them after the app closes.
    • Returns the user to the task list.

    The only problem is that newly created tasks appear in the wrong order.

    If AI knows only that “task creation needs fixing,” it has a much wider area in which to make changes.

    If it knows that creation and storage already work correctly, it can narrow its attention to ordering.

    A useful statement might be:

    “Task creation and persistence are already working. The problem is only the order in which tasks appear.”

    That protects working behaviour from unnecessary modification.

    The more precisely you identify what is already correct, the less reason AI has to rebuild it.

    Context Should Include What Must Not Change

    Sometimes the most valuable sentence is:

    “Do not change…”

    Suppose you are adjusting how the home screen displays task progress.

    You might say:

    “Do not change how progress is calculated. Only the way it is displayed needs work.”

    That establishes a boundary.

    This is particularly helpful when several parts of the project are connected.

    AI may notice what appears to be an easier solution by changing another piece of the system.

    Without context, it does not know whether that is acceptable.

    You should not freeze the entire project unnecessarily. Sometimes AI will discover that a broader change genuinely is needed.

    In that situation, a good response is not to ignore the constraint.

    It is to explain the conflict:

    “The requested behaviour cannot be implemented reliably without changing this part. Here is why.”

    That gives you a decision to make instead of silently changing the architecture.

    More Context Is Not Always Better

    Once people understand that AI needs context, another mistake can appear:

    They give it everything.

    Entire conversations.

    Every project file.

    Long documents unrelated to the task.

    A complete history of every decision ever made.

    This can make the important information harder to identify.

    Good context is relevant context.

    Consider a change to the task editing screen.

    AI may need to know about:

    • The task data structure.
    • How tasks are saved.
    • The existing editing screen.
    • Validation rules for task titles.
    • The behaviour expected after saving.

    It probably does not need detailed information about your app’s onboarding animation.

    The right question is not:

    “What information can I give AI?”

    It is:

    “What information could change the correct solution?”

    That is a much stronger filter.

    Context Can Become Outdated

    Another danger is giving AI information that used to be true.

    Perhaps an earlier conversation says:

    “The app does not support recurring tasks.”

    Two weeks later, recurring tasks are added.

    If AI receives the old statement without the new project state, it may make decisions based on outdated information.

    This is why current code and current behaviour should usually take priority over remembered descriptions when they conflict.

    When necessary, tell AI explicitly:

    “Some earlier discussions may be outdated. Treat the current project files and the requirements below as the current source of truth.”

    Projects evolve.

    Your context must evolve with them.

    Preserving project decisions, which we will cover in Blog 14, is partly about keeping that knowledge useful rather than merely accumulating more of it.

    Let AI Identify Missing Context

    You do not always know what information matters.

    That is normal.

    You can ask AI to identify what it needs before implementation.

    For example:

    “Before making changes, inspect the relevant code and tell me whether you are missing any information that would materially affect the implementation.”

    Or:

    “What assumptions would you have to make if you implemented this now?”

    This is a powerful question.

    Assumptions reveal missing context.

    If AI says:

    “I would need to assume that completed tasks remain editable,” you now know there is a product decision that needs clarification.

    If it says:

    “I would assume that the existing local storage should continue to be used,”

    you can confirm or correct that assumption.

    The aim is not to eliminate every possible unknown.

    It is to surface unknowns that could change the result.

    Match The Amount Of Context To The Consequence

    Not every change requires a project briefing.

    If you ask AI to adjust a small amount of spacing on one screen, the relevant context may be tiny.

    If you ask it to change how user information is stored, the consequences are much larger.

    The necessary context grows with the scope and risk of the decision.

    A useful rule is:

    The more parts of the app a change can affect, the more existing context AI should understand before making it.

    For a small visual change, perhaps one file is enough.

    For a change to storage, AI may need to understand:

    • The data being stored.
    • Where it is currently stored.
    • Which parts of the app read it.
    • Which parts modify it.
    • What happens to information users already have.
    • Existing constraints around storage.

    You do not need the same process for every task.

    You need enough context for the consequences involved.

    A Practical Context Check

    Before asking AI to work on an existing feature, you can quickly check whether it understands five things:

    1. What is this part of the app for?

    Give the relevant product purpose when it affects the decision.

    2. How does it work now?

    Describe or let AI inspect the current implementation and behaviour.

    3. What existing decisions matter?

    Mention important architecture choices, constraints, dependencies, or requirements.

    4. What evidence do we have?

    For a problem, provide what actually happens rather than only your theory about why.

    5. What must remain intact?

    Identify working behaviour or project boundaries that should not be casually changed.

    You will not need every item every time.

    They are questions to help you notice missing context, not a form that must be completed before writing code.

    Do Not Make AI Guess The Project

    Vibe coding becomes much more reliable when AI does not have to reconstruct your project from fragments and assumptions every time you ask for something.

    Your job is not to explain every line of code yourself.

    It is to make sure the AI has access to the facts that matter.

    That may mean providing:

    • Relevant project purpose.
    • Existing behaviour.
    • Relevant code.
    • Technical constraints.
    • Previous decisions.
    • Evidence about a problem.
    • Working behaviour that must remain intact.

    Sometimes you provide those facts directly.

    Sometimes you ask AI to inspect the project and establish them first.

    Either way, the principle is the same:

    Understand the situation before changing the situation.

    Giving AI good context does not guarantee good code.

    It does something more fundamental: it reduces the number of important things the AI has to guess.

    Once the context is clear, the next challenge is deciding exactly what you want AI to do with it.

    In Blog 12, we will look at how to turn an intention into a clear coding request, including the change you want, its boundaries, the expected behaviour, and the questions that should be resolved before implementation.

  • How To Choose Software Dependencies For Your App

    A good software dependency solves a problem without creating more responsibility than the value it provides.

    Modern apps rarely create every capability themselves. They use software, services, and platform features that already exist. This can make an app far faster and more practical to build.

    The important decision is not simply whether a dependency works today.

    You also need to consider whether it fits your project, what happens if it changes or becomes unavailable, what it may cost, who maintains it, and how difficult it would be to replace.

    When AI can add a dependency in seconds, these questions become especially important. Installation may be easy. Living with the decision can last much longer.

    Every Dependency Gives You Something And Asks Something In Return

    In the previous blogs, we saw that a project can rely on other software and that an app may communicate with external services.

    You do not need to relearn those concepts here.

    What matters now is the trade-off they create.

    Suppose you want to add maps to an app. You could use an existing mapping service rather than collecting geographic information and building a mapping system yourself.

    That dependency could give you:

    • Map data.
    • Search for places.
    • Directions.
    • Frequently updated geographic information.
    • Years of engineering work that you do not need to reproduce.

    In return, your app may now depend on:

    • The service remaining available.
    • Its interface continuing to work with your app.
    • Its prices remaining acceptable.
    • Its terms continuing to allow your use.
    • A network connection for features that need the service.

    This does not make the dependency a bad choice.

    It shows why the choice deserves thought.

    A useful dependency removes work you should not need to do yourself. A poor dependency can create ongoing work that was not obvious when it was added.

    First Ask Whether The Dependency Is Necessary

    Adding existing software is often much better than rebuilding a solved problem.

    However, not every problem requires another dependency.

    Sometimes your project, programming language, operating system, or existing tools already provide the capability you need.

    Imagine the personal errands app from earlier blogs needs to arrange tasks by date.

    AI might suggest installing another software package to handle dates.

    Before accepting that suggestion, you could ask:

    “Can the tools already used by this project handle this requirement reliably without adding another dependency?”

    If they can, another dependency may provide little benefit.

    This matters because every additional dependency becomes another part of the project that may need attention.

    But the opposite mistake is also possible.

    You could spend weeks building a complicated capability yourself even though dependable software already solves the problem well.

    The goal is not to have the fewest dependencies possible.

    It is to have dependencies that earn their place.

    Compare The Cost Of Depending With The Cost Of Building

    When deciding whether to use existing software, compare both sides of the decision.

    Building something yourself gives you more control over how it works. It may also avoid relying on another organisation.

    But control has a cost.

    You become responsible for creating the capability, finding its problems, maintaining it, adapting it when the rest of the app changes, and understanding unusual cases that an established solution may already handle.

    Consider an app that needs to process payments.

    Writing a screen that asks for payment details is only a tiny part of the real capability. Payment processing involves communication with financial systems and responsibilities that extend far beyond displaying the interface.

    Using an established payment provider may therefore remove enormous amounts of work.

    For a much smaller capability, the calculation may be different.

    If your app needs to convert a temperature between Celsius and Fahrenheit, depending on an external service to perform that simple calculation would probably add more complexity than it removes.

    The useful question is:

    “What work and responsibility does this dependency remove, and what work and responsibility does it introduce?”

    Do not compare an existing solution with the effort required to produce the first working version of your own solution.

    Compare it with the effort required to own your solution over time.

    Make Sure It Fits Your Project

    A dependency can be useful and well made but still be unsuitable for your app.

    It needs to work with the environment you are actually building for.

    For example, a dependency might:

    • Require a newer operating system than you intend to support.
    • Depend on a different version of another part of your project.
    • Work on one platform but not another.
    • Require capabilities your chosen environment does not provide.
    • Conflict with software you already rely on.

    Compatibility therefore needs to be considered before adoption, not discovered only after the app has been built around the dependency.

    AI can help investigate this.

    Instead of asking only:

    “Can I use this package?”

    Ask:

    “Is this dependency compatible with the versions, platforms, and requirements of this project? Explain any changes I would need to make to adopt it.”

    The second question considers the dependency in context.

    That is the recurring principle throughout this series: a technically possible solution is not automatically the right solution for your app.

    A Dependency Can Bring Other Dependencies With It

    The software you add may rely on other software itself.

    This creates a dependency chain.

    You might directly choose one package, while that package depends on several others to perform its work.

    You do not need to study every dependency inside every project before making progress. Modern software commonly builds on layers of existing work.

    But you should recognise the consequence.

    Adding one dependency does not always mean adding only one moving part.

    A large package introduced for a very small capability may bring considerable complexity with it.

    This is another reason to ask what the project already provides and whether the dependency is proportionate to the problem.

    AI can help you inspect this before accepting a suggestion:

    “What does this dependency bring into the project, including important dependencies of its own? Is that reasonable for the capability we need?”

    The aim is not to fear dependency chains.

    It is to avoid creating them without knowing they exist.

    Consider What Happens When A Dependency Changes

    Your code can remain exactly the same while something it depends on changes.

    A software package may release a new version. An operating system may change. An external service may alter the way requests work. A provider may stop supporting an older capability.

    Sometimes those changes require nothing from you.

    Sometimes your app must adapt.

    This means a dependency is not only a decision made when the feature is first built. It can become an ongoing relationship between your project and something outside that project.

    Before relying heavily on something, consider whether it appears to be actively maintained and whether the project or organisation behind it gives you reasonable confidence that it will continue to exist.

    There are no guarantees.

    Even widely used technologies can change direction or disappear.

    The goal is not to predict the future perfectly. It is to notice when an important feature depends on something whose future you do not control.

    The more important the dependency is to your app, the more this matters.

    Ask What Happens If It Becomes Unavailable

    Some dependencies are only needed while developing the app. Others are needed whenever the app performs a particular action.

    For those that affect the running app, consider what happens when the dependency cannot be used.

    Suppose your errands app eventually adds a feature that suggests nearby shops using an external location service.

    If that service is temporarily unavailable, perhaps the rest of the app can continue working while nearby suggestions cannot be loaded.

    That is very different from designing the entire app so that it cannot open unless the service responds.

    The dependency is the same. Its importance to the overall experience is different.

    Think about criticality: how much of your intended result depends on this thing working?

    A dependency supporting an optional convenience deserves different attention from one supporting the main reason the app exists.

    You can ask:

    “If this dependency stops working temporarily, which parts of the app still work and which do not?”

    That question makes the consequence visible before failure forces you to discover it.

    Later blogs will cover testing unsuccessful situations in more detail. For now, the design decision is to understand how much control you are handing to the dependency.

    Understand How Cost Can Change

    Some dependencies are free to use. Others charge according to requests, users, stored information, processing time, or another measure of usage.

    A service that costs almost nothing while you have ten users might become expensive when you have ten thousand.

    That does not mean you should avoid services with usage-based pricing.

    It means today’s price does not tell you the whole story.

    Suppose an AI-powered feature sends each request to an external model provider.

    Your early testing may cost only a few dollars because you are the main user.

    If the app becomes popular, every additional person may generate more requests and therefore more cost.

    You should understand:

    • What causes the cost to increase.
    • Whether there are fixed and variable charges.
    • Whether there are usage limits.
    • What happens when a limit is reached.
    • Whether the expected cost still makes sense for the value the feature provides.

    Exact prices can change, so this is not about memorising them.

    It is about recognising cost behaviour as part of the technical decision.

    A capability is not truly free to your project merely because its first bill is zero.

    Think About Maintenance Before Adoption

    Dependencies can save maintenance because somebody else is responsible for much of the underlying capability.

    They can also create maintenance because your project has to remain able to work with them.

    Imagine that you install a package and never need to think about it again for several years.

    That is a very different dependency from one requiring frequent project changes merely to remain compatible.

    Before adopting something important, useful signals include whether it is actively maintained, whether changes are explained clearly, and whether people using it have a reasonable path between versions.

    Popularity can provide useful evidence, but popularity alone does not prove suitability.

    A widely used dependency may still be wrong for your project. A smaller dependency may be perfectly reasonable if its purpose is narrow, its implementation is understandable, and you can replace it without great difficulty.

    Blog 28 will return to dependency maintenance as part of improving an app over time.

    At this stage, understand that adoption creates the possibility of future maintenance.

    Ask How Difficult It Would Be To Replace

    One of the most useful dependency questions is rarely asked when everything is working:

    “What would we need to change if we stopped using this?”

    The answer tells you something about replacement cost.

    Imagine two versions of the errands app use an external location service.

    In one version, most of the app uses your own simple representation of a location. One part of the project communicates with the provider and translates its responses into that representation.

    In another version, provider-specific information and assumptions have spread throughout the app.

    Both approaches may work.

    But changing providers later could be much harder in the second project because the dependency has become deeply connected to unrelated parts of the software.

    This is sometimes described as lock-in: changing away from a technology or provider becomes difficult or costly because the system has become strongly tied to it.

    Some lock-in is reasonable.

    If an external service provides enormous value and replacing it is unlikely to matter, deeper integration may be worth the trade-off.

    The important thing is to recognise the trade-off rather than discovering it only when you want to leave.

    Consider What The Dependency Controls

    Replacement becomes especially important when the dependency holds something your app cannot easily recreate.

    For example, ask whether it controls:

    • Important saved information.
    • User accounts.
    • A central capability of the product.
    • A format that other parts of the app depend on.
    • Access to something that cannot easily be moved elsewhere.

    The more important the dependency is, the more useful it is to understand your alternatives.

    This does not mean duplicating every external system “just in case.”

    That can waste enormous amounts of effort.

    Instead, know what you would lose if the dependency disappeared and whether there is a realistic route to another solution.

    Questions about personal information, permissions, secrets, and what may be sent to external systems deserve their own deeper treatment. We will cover those in Blog 18. Legal obligations created by particular services or uses will be addressed in Chapter 7.

    The immediate lesson is simpler: understand what part of your app another system is being allowed to control.

    Do Not Let AI Choose Dependencies Silently

    AI can make dependency decisions look smaller than they are.

    You might ask:

    “Add charts to this screen.”

    The AI installs a charting package and completes the feature.

    From the perspective of the visible screen, your request has been satisfied.

    From the perspective of the project, another decision has also been made: the app now relies on that package.

    That decision should not be invisible to you.

    When AI proposes adding something new, ask it to explain:

    “Why is this dependency necessary? What does it provide that the project does not already have?”

    Then ask:

    “What are the important consequences of adopting it, including compatibility, maintenance, cost, availability, and how difficult it would be to replace?”

    You do not need a lengthy investigation for every tiny addition.

    The depth of the decision should reflect its consequences.

    A dependency used for an optional visual effect does not deserve the same investigation as the service that stores all of your users’ information.

    Keep The Decision Proportionate

    There is a danger on both sides.

    You can accept every dependency AI suggests and slowly lose understanding of what your app relies on.

    Or you can become so afraid of dependencies that you rebuild ordinary capabilities and never finish the product.

    Neither approach is strong engineering.

    A better approach is proportional.

    For a small, replaceable dependency, you may need only a quick check that it is suitable and compatible.

    For something central to the product, you may investigate:

    • Why you need it.
    • What alternatives exist.
    • Whether it fits the project.
    • What it costs.
    • Who maintains it.
    • What happens when it is unavailable.
    • How deeply the app will rely on it.
    • How difficult moving away would be.

    The consequences determine how much investigation is worthwhile.

    Record Important Dependency Decisions

    Some decisions will be obvious when you make them and mysterious six months later.

    If a dependency is important, record briefly why you chose it.

    For example:

    “We use the external mapping service for nearby shop suggestions because maintaining geographic search ourselves is outside the purpose of this app. The main errands experience does not depend on the service, so tasks remain available if it cannot be reached.”

    That short explanation preserves several useful decisions:

    • Why the dependency exists.
    • What responsibility it removes.
    • Where it fits into the app.
    • How important it is to the main experience.

    You do not need to create documentation for every minor package immediately.

    Preserve decisions whose reasoning would matter if the project changed later.

    We will develop this habit further in Blog 14 when we look at maintaining project knowledge across conversations and development sessions.

    Use Dependencies To Reduce Work, Not Understanding

    Dependencies are one of the reasons a small team—or one person working with AI—can build capable software.

    You can use decades of existing work instead of recreating everything from first principles.

    That leverage is valuable.

    But outsourcing the capability does not outsource the decision.

    You still need to understand enough to judge why the dependency belongs in your project and what relying on it means.

    When considering one, ask:

    “What does this give the app, what does it make the app rely on, and is that trade-off worth it?”

    That question keeps the decision connected to the product you are actually trying to build.

    With this blog, we have completed the basic picture of the software itself: its main responsibilities, the movement of information, how the project becomes running software, and what that software relies on.

    In Chapter 4, we will turn that understanding into better work with AI.

    Blog 11 begins with a fundamental question: what does AI need to know about your project before it can make a good change?

  • How Does Code Turn Into an App?

    Code becomes a working app when development tools prepare its instructions and a device or other computer runs them.

    The exact process depends on the kind of software you are building. A phone app, a website, and a program running on a server may use different tools. They all connect the code you write or generate with an environment that can carry out its instructions.

    Understanding that connection helps explain why receiving code, building successfully, and seeing an app run are separate stages.

    Start With The Project Files

    The code people write and edit is called source code. It is usually kept in source files within a project.

    A project may also contain:

    • Images, icons, and other visual material.
    • Settings that control how the app is prepared.
    • Information about software the project depends on.
    • Files used to check its behaviour.

    Together, these provide the material and instructions needed to prepare the app.

    When AI gives you code in a conversation, that code is still a proposed addition or change. It needs to reach the correct place in the project before it can affect the app.

    Some AI tools can edit project files directly. In other situations, you may need to transfer the code yourself. Either way, the version in the project is what the development tools work with.

    Development Tools Prepare And Run The Software

    A code editor lets you view and change source files.

    A larger development application may combine an editor with tools for building, running, and investigating the software. This is often called an integrated development environment, or IDE.

    You do not need to memorise that term. The useful idea is that one application may provide several different functions.

    Editing changes the project. Building prepares it. Running lets you interact with the result.

    These actions may happen together when you press a single button, but they still do different jobs.

    What Does It Mean To Build An App?

    A build is the process of preparing the project for use in a particular environment.

    Depending on the project, building may involve:

    • Checking whether the code follows the language’s rules.
    • Translating code into another form.
    • Combining different parts of the software.
    • Preparing images and other resources.
    • Producing files that can be installed or served to users.

    One part of this process may be compilation. A compiler translates source code into another form that the system can use.

    The details vary. Some code is translated before the app runs. Other code is processed by supporting software while it runs. Development tools may also perform preparation automatically as you make changes.

    The foundation to understand is that the editable project and the running app are connected through these tools and processes.

    Running Means Carrying Out The Instructions

    When you run an app, the computer begins carrying out its instructions.

    This is when the interface appears, actions become available, and the app starts responding to information.

    The software and conditions that support this process form its running environment. You may also hear the term runtime used for the supporting software that helps execute code.

    For example:

    • A phone app runs with support from the phone’s operating system.
    • A web app may run partly in a browser.
    • A backend runs on a computer that responds to requests from other software.

    An app can involve more than one environment. A browser screen might communicate with a backend running elsewhere.

    This means getting one part running does not necessarily mean every connected part is available.

    A Successful Build Answers A Limited Question

    A successful build shows that the project passed the checks and preparation required by that build process.

    It does not establish that every feature behaves correctly.

    A recipe app might build successfully while calculating the wrong ingredient amounts. The instructions can be acceptable to the development tools and still fail to produce the result you intended.

    Different problems can appear at different stages:

    • Build problem: The tools cannot prepare the project.
    • Running problem: The app starts but encounters a failure while being used.
    • Behaviour problem: The app continues running but produces an incorrect result.

    Recognising the stage helps you describe what happened more precisely.

    “The build failed with this message” gives a different starting point from “the app opens, but selecting four servings shows quantities for two.”

    Later blogs will explain how to investigate those problems.

    Make Sure You Are Looking At The Changed Version

    Editing a source file does not always update the version currently running.

    Some development tools refresh changes automatically. Others require another build, restart, or installation. The behaviour depends on the tools and the type of change.

    Imagine you change a button label from Save to Save recipe, but the screen still shows Save.

    Before asking AI to rewrite the change, consider whether the running app includes it.

    You might be viewing:

    • A previously installed version.
    • Another copy of the project.
    • A preview that has not refreshed.
    • A different build from the one you just prepared.

    Knowing which project you changed and which version you are running prevents confusion between an unsuccessful change and an outdated result.

    Why Can The Same App Behave Differently Elsewhere?

    Software runs under particular conditions.

    Those conditions include the device, operating system, browser, permissions, network connection, available services, and information already stored.

    Your development setup may contain saved recipes and permissions you granted earlier. Someone opening the app for the first time may have neither.

    A computer-based simulator can help you explore a phone app. A real phone may introduce different screen dimensions, hardware behaviour, and resource limits.

    Project settings can also affect behaviour. For example, a development version might connect to a test service while a released version connects to the service used by real users.

    When behaviour differs, the environment is part of the information worth investigating. “It works on my computer” describes one observation under one set of conditions.

    Learn The Path For Your Own Project

    You do not need to become an expert in every development tool. Begin by understanding the path your project follows.

    You should be able to explain:

    • Where the source files are.
    • Which tools prepare the app.
    • How to start the version you changed.
    • Where it runs.
    • Whether other software must be available for it to work.

    A useful request to AI is:

    “Explain how this project goes from source files to a running app. Identify what prepares it, where it runs, and how I can tell whether I am using the latest changes.”

    That explanation gives you a practical connection between the code you edit and the software you observe.

    In the next blog, we will examine the other software your app may rely on, including libraries, services, and APIs, and what those dependencies mean for your project.

  • How Does Data Flow Through an App?

    Data flow describes how information enters an app, moves through its rules, and becomes a result. Some information is also saved so it can be used again later.

    Following that journey helps you understand what the software is doing behind the screen. It also helps explain problems such as incorrect results, outdated displays, and changes that disappear.

    In the previous blog, we explored the main parts of an app. Now we will connect those parts by following the information they handle.

    Start With Where The Information Comes From

    An app needs information to work with. This is often called input.

    Input can come from these places:

    • Something a person types or selects.
    • Information the app previously saved.
    • A device feature, such as a camera.
    • A response from an external service.

    Consider a simple recipe app. A recipe serves two people and requires 100 grams of rice. The person selects four servings because they are cooking for more people.

    The app now has information from two sources: the original recipe and the person’s selection.

    Understanding those sources matters. If the displayed amount of rice is wrong, the problem could involve the original recipe, the selected number of servings, or the calculation that connects them.

    Follow What The App Does With The Input

    After receiving information, the app applies its rules.

    In the recipe example, the rule is to adjust ingredient amounts in proportion to the number of servings.

    Moving from two servings to four doubles the amount of rice:

    Original recipe: 100 grams for two servings.
    Selected amount: Four servings.
    Calculated result: 200 grams.

    This is a transformation: the app uses existing information to produce something new.

    Transformations can take many forms. An app might calculate a total, filter a list, sort results, or turn entered text into a saved record.

    Before performing that work, it may also check whether the input is acceptable. For example, the recipe app needs a rule for handling zero servings or an entry that is not a number.

    These checks connect the requirements you defined earlier with the information the app receives.

    Understand What “State” Means

    You will often hear developers and AI tools refer to an app’s state.

    State means the information describing the app’s current situation.

    In the recipe app, state might include:

    • Which recipe is open.
    • How many servings the person selected.
    • Whether recipe information is still loading.
    • Whether an error message needs to be shown.

    State changes as the person uses the app or new information arrives.

    For example, selecting four servings changes the current serving amount. The app should then update the ingredient quantities shown on the screen.

    This connection explains a common kind of problem: the information changes, but the display does not update with it. The screen may continue showing the quantities for two servings even though four are selected.

    The calculation and the display are connected responsibilities. Both need to reflect the same current situation.

    Separate Original Information From Calculated Results

    Some information is a starting point. Other information is worked out from it.

    In our example:

    • The original recipe specifies 100 grams for two servings.
    • The person selects four servings.
    • The app calculates 200 grams.

    The calculated quantity depends on the original recipe and the current selection. It is sometimes called derived data, meaning information worked out from other information.

    Keeping this relationship clear prevents mistakes.

    Suppose the app replaces the original quantity with 200 grams but still treats it as the amount for two servings. A later calculation could scale an amount that was already adjusted.

    The original recipe no longer provides a reliable starting point.

    A useful question to ask AI is:

    “Which information stays as the original record, and which values are calculated from it?”

    There are situations where calculated results are saved. The important point is knowing which information each result depends on and when it must be updated.

    Decide What Needs To Survive Closing The App

    Information held in working memory can be used while the app runs. It does not, by itself, provide reliable long-term storage.

    Persistent information is information saved so it remains available beyond the current running session.

    The recipe app might need to preserve a person’s saved recipes. Whether it should remember their most recent serving selection is a separate design decision.

    Some state may be temporary:

    • Which menu is open.
    • Whether a message is being displayed.
    • An unfinished selection that the person chose to cancel.

    Other state may need to be saved:

    • A recipe the person created.
    • A change they confirmed.
    • A preference the app promises to remember.

    There is no universal rule that every change should be saved. The decision follows the intended behaviour.

    What matters is that the app reliably keeps what people expect it to keep.

    Remember That Saving And Displaying Are Different Events

    Information can appear on a screen before it has been successfully saved.

    Likewise, information can be saved correctly while a screen continues showing an older version.

    Imagine someone changes a saved recipe’s name. The editing screen shows the new name, but the recipe list still shows the old one.

    Some of these explanations are possible. The change might not have been saved. The list might not have retrieved the updated information. Or it might still be displaying an old copy.

    Following the information helps you distinguish these possibilities:

    What did the person enter? What did the app accept? What was saved? What information is the list displaying?

    You do not need to diagnose the code immediately. First, understand which parts of the information journey agree and where they differ.

    Trace One Piece Of Information At A Time

    A useful way to understand an unfamiliar app is to follow one value through it.

    For the recipe’s serving amount, the description might be:

    “The person selects four servings. The app keeps that selection as its current state, compares it with the recipe’s original two servings, calculates the adjusted quantities, and displays them.”

    Then ask a separate question about storage:

    “Should that selection still be four when the person returns tomorrow?”

    This approach keeps the explanation manageable. It also avoids assuming that every value follows exactly the same path. Some information is displayed immediately, some is calculated, and some is saved or retrieved later.

    You can ask AI:

    “Trace the selected serving amount through this app. Explain where it comes from, what uses it, what gets displayed, and whether it is saved. Use simple language.”

    Understanding these connections makes the software easier to reason about. In the next blog, we will follow a different journey: how the project’s code becomes an app you can actually run.

  • How Does an App Work? The Main Parts Explained

    An app works by connecting what a person sees and does with instructions that produce a result. Depending on its purpose, it may also save information or communicate with other software.

    You can understand this through four common responsibilities:

    • The interface: What people see and interact with.
    • The application logic: The rules and decisions behind the behaviour.
    • Data storage: How information is kept for later.
    • External services: Other systems the app communicates with when needed.

    These are a useful way to think about an app. They do not mean every project needs four separate files or every app needs an external service.

    Understanding these responsibilities helps you discuss changes with AI and recognise which parts may be involved when something goes wrong.

    The Interface Lets People Use The App

    The user interface, often shortened to UI, is the part through which someone interacts with the software.

    It includes things such as:

    • Text, images, and lists.
    • Buttons and input fields.
    • Menus and navigation.
    • Messages explaining success, progress, or failure.

    In our errands app, the interface includes the field where someone enters a task, the “Add task” button, and the list showing saved errands.

    The interface has two closely connected jobs. It receives the person’s actions and communicates what the app is doing.

    For example, pressing “Add task” tells the app that the person wants to create a task. Seeing the task appear in the list communicates the result.

    The screen alone does not explain how that result happened. Other instructions decide whether the task is allowed and how it should be saved.

    Application Logic Gives Actions Their Meaning

    Application logic means the rules and decisions that control the app’s behaviour.

    In the previous blog, we defined requirements such as:

    “A task must have a name. A name containing only spaces counts as empty.”

    The application logic puts that rule into practice. It determines whether the entered text is acceptable before allowing the task to be created.

    Logic can also handle calculations, comparisons, and decisions about what should happen next.

    For example:

    • A shopping app calculates a basket total.
    • A recipe app decides which recipes match a search.
    • A task app decides whether a new entry meets its rules.

    The interface and logic work together. If someone presses Add task without entering a name, the logic identifies the problem. The interface explains it to the person.

    Keeping this distinction in mind helps you understand why a button can look correct while producing the wrong behaviour.

    Data Storage Keeps Information Available

    Data is the information an app works with. In the errands app, it includes task names and the details needed to keep those tasks organised.

    Some information only needs to exist briefly. Other information needs to remain available after the app closes.

    Data storage provides a way to keep that information and retrieve it later.

    The errands app needs storage because its purpose includes finding tasks again. Without it, a task might appear during the current session and disappear when the app restarts.

    Information can be stored in different places, including:

    • On the person’s device.
    • On a remote computer reached through a network.
    • In both places, with a system for keeping the copies consistent.

    A small app can store information on the device without requiring an online account.

    When you hear the word database, think of an organised store of information that software can read and change. A database is one storage option; it does not automatically mean the information is kept online.

    The important questions are what needs to be kept, where it is kept, and how the app retrieves it.

    External Services Provide Capabilities From Elsewhere

    An external service is another system that the app communicates with.

    For example, a weather app might request a forecast from a weather service. A delivery app might use a mapping service to show an address.

    The app needs a way to make a request and understand the response. You will often hear this connection described as an API, short for application programming interface. It is a defined way for one piece of software to interact with another.

    An external service can provide a useful capability without you building the whole system yourself. It also introduces another part the app depends on.

    For example, a weather screen needs to account for the possibility that a forecast cannot be retrieved.

    Our first errands app can record and retrieve tasks on the device. An external service would become relevant if the agreed experience required something beyond that, such as sharing information between people.

    We will examine dependencies and their trade-offs more closely in Blog 10.

    What Do “Frontend” And “Backend” Mean?

    These terms describe broad parts of many software systems.

    The frontend is the part a person interacts with, such as a website in a browser or an app on a phone. It can include interface code and logic that runs on the person’s device.

    The backend is the supporting software that runs elsewhere, often on a server. A server is a computer or program that responds to requests from other software.

    A backend might manage shared information, check account access, or perform work needed by several users.

    These terms do not divide every responsibility neatly. Some rules may run on the device, while others run on a server. Information may also be stored in either place.

    An app that performs its work entirely on the device may not need a separate backend. Its purpose determines what supporting systems are necessary.

    Responsibilities Are Different From Files And Screens

    A screen is something the person sees. A file is a place where project material, such as code, is organised. A responsibility describes what a part of the app does.

    They do not always match one another directly.

    One screen may rely on several files. A single saving component may support many screens. A small project may initially put several responsibilities together.

    For example, the task list screen might use:

    • Interface code to display the list.
    • Logic to decide its ordering.
    • Storage code to retrieve the saved tasks.

    Later in the book, we will discuss how to organise code clearly. For now, focus on understanding the jobs involved rather than memorising a particular folder arrangement.

    Use This Picture When Talking To AI

    Knowing these parts gives you more precise questions to ask.

    If the task list is hard to read, you can discuss the interface. If blank tasks are being accepted, you can ask about the rule that checks task names. If tasks disappear after restarting, you can investigate saving and retrieval.

    These are starting points for investigation, rather than proof of where a fault lies. A visible problem can have a cause in another part of the app.

    A useful request is:

    “Explain which parts of this project display tasks, apply the task rules, and save or retrieve information. Tell me whether any external service is involved.”

    You now have a basic picture of the system behind the screen. In the next blog, we will follow a piece of information through that system—from what a person enters to what the app changes, displays, and keeps.

  • How To Write App Requirements AI Can Understand

    To write clear app requirements, describe what should happen when someone uses each feature. Include the expected result, the rules that apply, and what the app should do when an action cannot be completed.

    A requirement is a statement of what the app needs to do or a condition it must meet.

    You do not need a long technical document to begin. A few clear statements and examples can remove uncertainty before AI turns your idea into code.

    In the previous blog, we chose the smallest complete experience for a personal errands app: enter a task, save it, and find it again later.

    Now we need to explain how those capabilities should behave.

    Turn Each Feature Into A Clear Description

    “Add a task” names a feature. It leaves several decisions open.

    Does pressing Add immediately create the task? Is a name required? What appears afterwards? What happens if the app cannot save it?

    A clearer description connects the person’s action with the expected result:

    “When the person enters a task name and presses “Add”, the app saves the task and displays it in the list.”

    This gives AI more direction. It also gives you something specific to assess later.

    The difference is useful across many kinds of apps:

    • Feature: Search recipes.
    • Requirement: When someone searches for an ingredient, show recipes that include it.

    Or:

    • Feature: Calculate a total.
    • Requirement: Add the prices of all items in the basket and display the combined amount.

    Each statement explains behaviour instead of simply naming a capability.

    Include The Starting Situation

    The same action can produce different results depending on what is already happening.

    Pressing Add with a task name entered is different from pressing it when the field is empty.

    A useful requirement can describe three connected parts:

    1. Situation: What is true before the action?
    2. Action: What does the person do?
    3. Result: What should happen afterwards?

    For example:

    “When the task name field is empty and the person presses “Add”, no task is created. The app explains that a task name is required.”

    This format helps you think through behaviour without needing to describe the code behind it.

    Use it where the starting situation makes a difference. There is no need to force every requirement into the same wording.

    Make Important Rules Explicit

    Rules determine which actions are allowed and how the app handles them.

    For our errands app, you might decide:

    • A task must have a name.
    • A name containing only spaces counts as empty.
    • Two tasks may have the same name.
    • Newly added tasks appear at the top of the list.

    These are design choices for this example. Another app could reasonably use different rules.

    What matters is making the choice clear.

    Consider duplicate names. A person might enter “Buy groceries” on two separate occasions. If you have not decided whether that is allowed, AI may reject the second task, replace the first, or create another one.

    An explicit rule removes that uncertainty:

    “Allow tasks with the same name. Adding a new task must not replace an existing task.”

    You are describing the behaviour you need. The technical approach can be worked out later.

    Explain What Happens When An Action Fails

    A requirement should cover ordinary situations in which the intended result cannot be delivered.

    For example, saving a task might fail. The app then needs to communicate what happened and leave the person in a useful position.

    You could specify:

    “If saving fails, explain that the task was not saved. Keep the entered text available so the person can try again.”

    That statement addresses connected concerns: accurate feedback, preserving the person’s effort, and providing a next action.

    Different situations need different responses:

    • An empty field may need an explanation of what to enter.
    • A search with no matches may need a clear “no results” message.
    • An unavailable service may need a retry option.
    • An action still in progress may need a visible indication that the app is working.

    You do not need to imagine every possible failure at this stage. Begin with the ordinary conditions that could prevent the central journey from working.

    Describe What People Should See And Understand

    Requirements also cover how the app communicates its state.

    Suppose someone opens the errands app for the first time. There are no tasks yet.

    “Display the task list” does not explain what that person will see. A more complete requirement might say:

    “When there are no tasks, display a message explaining that the list is empty and show where to add the first task.”

    This helps someone understand the screen without needing instructions from you.

    Avoid vague wording such as “make it intuitive” when you can describe the intended behaviour. For example:

    “Label the task creation button Add task.”

    You can still refine the wording or appearance later. The requirement makes the immediate intention understandable.

    Record Constraints That Affect The Solution

    A constraint is a limit the solution must work within.

    For the first version of our example app, you might specify:

    “People must be able to add and view their errands without an internet connection.”

    That affects which approaches can support the experience.

    Other constraints could concern supported devices, accessibility needs, or what information the app may collect. Record those that matter to the agreed scope.

    Some constraints may require further research. If you do not yet know which accessibility or privacy requirements apply, record that as an open question rather than allowing an assumption to become a settled decision.

    Use Examples To Remove Ambiguity

    Examples make a requirement easier to interpret.

    For a basket total, “calculate the correct amount” leaves the expected result unstated. A concrete example is clearer:

    “If the basket contains one item priced at $10 and another priced at $5, the displayed total is $15.”

    You can add examples that reveal an important rule:

    “If the basket is empty, display a total of $0.”

    These examples provide reference points for later checks. They do not cover every possible situation, but they help establish what the requirement means.

    The observable conditions used to decide whether work is complete are often called acceptance criteria. In plain language, they describe what must be true for you to accept the result.

    Keep Decisions And Open Questions Separate

    You will not know every answer immediately.

    A short requirements note can distinguish:

    • Decided: Tasks need a non-empty name.
    • Decided: Tasks with identical names are allowed.
    • Open question: Should there be a limit on task-name length?

    This helps AI recognise where it should follow a decision and where clarification or a suggestion is still needed.

    You can ask:

    “Review these requirements for missing decisions or conflicting rules. List any assumptions you would need to make before building.”

    Use the response to improve the description while keeping the first version’s agreed scope in view.

    You now have three connected foundations: the result the app should deliver, the experience included in its first version, and the behaviour that experience requires.

    In Chapter 3, we will look at the main parts of an app and how they work together to deliver those requirements.

  • How To Choose Features For Your App’s First Version

    To choose features for your app’s first version, follow the person’s journey from their starting point to the result they need. Include what makes that journey work, then set aside additions that can wait.

    This gives you a practical way to decide what belongs in the app without trying to build every useful idea at once.

    In the previous blog, we described an app’s purpose. Our example was a task app that helps someone record personal errands and find them later when they are ready to act.

    Now we need to decide what that experience requires.

    Describe The Complete Journey

    A list of features does not explain how someone will use them together.

    “Task entry, storage, and a task list” names three parts. A journey connects those parts through the person’s experience:

    1. They open the app when an errand comes to mind.
    2. They enter the task.
    3. The app saves it and shows that it was added.
    4. They leave the app.
    5. They return later and find the task.

    This journey reveals why each part matters.

    Saving is necessary because the person needs the information later. Showing the added task helps them know their action succeeded. A way to find it again completes the experience.

    Without one of these connections, you might build several features while leaving the person unable to achieve the intended result.

    Decide Where The Experience Begins And Ends

    The starting point should reflect the person’s actual situation.

    A new user may have an empty app with no tasks. If the experience only makes sense after you have filled it with example information, the beginning is incomplete.

    The ending should connect to the result you promised.

    For our example, the immediate result is being able to record an errand and retrieve it later. If the app also promises to help people track unfinished errands, the journey extends to marking tasks as finished and keeping their status clear.

    Both are reasonable choices. What matters is knowing which experience you are providing.

    That boundary helps you avoid adding features simply because other task apps have them.

    Find The Simplest Way To Support Each Part

    Once the journey is clear, look for a straightforward way to support it.

    Someone needs to enter a task. A text field and an Add button may be enough.

    Someone needs to find saved tasks. A single readable list may be enough for the limited experience you are planning.

    You could also imagine voice entry, automatic categories, advanced search, and several ways to arrange the list. Each might eventually help, but you need a reason to include it now.

    A useful question is:

    “If this feature were absent, could the intended person still complete the journey reasonably?”

    If the answer is yes, the feature may be able to wait.

    “Reasonably” matters. Technically allowing someone to reach a result through a confusing or exhausting process does not make the experience complete enough to be useful.

    Separate Essential Features From Later Improvements

    For an initial version focused on recording and retrieving personal errands, the essential capabilities might be:

    • Enter a task.
    • Save it.
    • See that it was added.
    • Find it again after reopening the app.

    Possible later improvements might include:

    • Grouping tasks into categories.
    • Adding recurring reminders.
    • Showing completion charts.
    • Sharing lists with other people.

    These are choices for this particular purpose, rather than a universal rule for task apps.

    If the purpose were to remind someone about an errand at a specific time, reminders would become central. If the app were intended for a shared household list, sharing would need attention from the beginning.

    A feature becomes essential because the intended experience depends on it.

    Its popularity in other apps does not establish that need.

    Consider What Each Feature Brings With It

    A feature can introduce work and decisions beyond what appears on the screen.

    Sharing a list, for example, raises questions about who can access it, who can change it, and what happens when two people edit it.

    Using the app on several devices raises questions about keeping information consistent between them.

    These may be worthwhile capabilities. Understanding what they require helps you judge whether they belong in the first version.

    The same applies to accounts. Some experiences need a way to identify each person. Others can provide their initial value without asking people to register.

    Choose based on what the journey requires. Each addition should earn its place through the role it plays.

    Keep Basic Care Inside The First Version

    Reducing features should still leave people with a clear and trustworthy experience.

    For example, if saving fails, the app should avoid showing a message that says the task was saved. If there are no tasks yet, the person needs to understand how to begin.

    Readable text, understandable controls, and appropriate protection for information also affect whether someone can use the app successfully.

    These concerns belong alongside the central journey. Treating them as optional decoration can leave the first version unable to serve its intended users.

    The next blog will explain how to describe correct behaviour, including unsuccessful actions. For now, allow room for the basic responses the experience needs.

    Write Down What Belongs Now And What Can Wait

    A short record helps keep the boundary clear when you start working with AI.

    For example:

    Purpose: Help someone record personal errands and find them later.

    Included in this version: Entering tasks, saving them on the device, displaying them in a list, and retrieving them after reopening the app.

    For later consideration: Reminders, shared lists, categories, and use across several devices.

    This makes the current plan easier to understand. It also preserves other ideas without turning all of them into immediate work.

    You can give this description to AI and ask it to identify missing parts of the journey before suggesting additions.

    If a new idea appears during development, compare it with the purpose and the agreed boundary. You can change the plan when there is a clear reason, while remaining aware of what that change adds.

    Small Enough To Manage, Complete Enough To Help

    The first version gives you a focused experience to build and understand. Its size depends on the result it needs to deliver.

    For the errands app, a modest set of connected capabilities can let someone record a thought and return to it later. That is a useful result even without a calendar, charts, or shared lists.

    Once you know which capabilities belong, the next question becomes more precise: how should each one behave?

    In Blog 6, we will describe the rules, expected results, and ordinary problems that AI needs to understand before implementing them.