Inside the work.
From the first brief to the systems behind it. Practical guides for people building and running software.

Find your next step.
Explore a topic, or search for a problem.
Search titles, topics, and the full text of every guide.
Guides organized around the decisions that shape software.
55 articles and updates
When does custom software make sense?
When to buy, when to build, and what it takes to own the result. A practical guide to choosing a first step.
Published 4 min readHow to write a software brief a team can actually use
A useful brief connects a problem to a decision. It gives a delivery team enough context to ask better questions without pretending every implementation choice is settled.
Published 5 min readDesign an internal tool around the next action
An internal tool earns its place by helping someone complete work. The most useful screen often makes the next decision obvious rather than presenting every available field equally.
Published 3 min readDefine the boundary before building an integration
Connecting two systems requires more than moving fields. The teams need to agree which system owns each fact, when changes become visible, and who resolves disagreements.
Published 3 min readDecide what an application health check should prove
A running process does not necessarily mean users can complete their work. Health checks should answer specific operational questions without making the system less stable when a dependency has trouble.
Published 3 min readPrepare a handover that lets another team operate the software
A handover succeeds when the receiving team can perform ordinary work and recover from a known problem without depending on the original developers being available.
Published 4 min readMap the workflow before you automate it
Automation makes a process repeatable. Before investing in it, establish which steps are intentional, which exist because of current tools, and which nobody can explain.
Published 3 min readAsk for the right information in a contact form
A contact form should gather enough information to route and answer an inquiry. Every extra required field should have a clear purpose in that first conversation.
Published 4 min readDesign a CSV import people can trust
A CSV file can look simple while carrying ambiguous dates, missing identifiers, and inconsistent categories. A useful import workflow makes those issues visible before they become application data.
Published 5 min readChoose monitoring signals that explain user impact
Monitoring should help a team notice a problem and decide where to investigate. Collecting many numbers is useful only when someone can connect them to the service people depend on.
Published 5 min readRecord why an architecture decision was made
Architecture documentation is most useful when it preserves the context behind a choice. A diagram can show the current arrangement without explaining why it was selected or when it should change.
Published 3 min readChoose a first release that answers a real question
A first release should make a useful task possible and expose an important uncertainty. A collection of disconnected screens can satisfy a feature checklist while teaching very little.
Published 3 min readUse empty states to explain what happens next
An empty screen can mean several things: no records exist, a filter matches nothing, data is loading, or access is restricted. Treating all of them as “Nothing here” leaves the user to guess.
Published 4 min readMake data exports useful outside the application
An export is a product interface used by another person or system. Its value depends on whether the recipient can understand the fields, reproduce the selection, and safely interpret the result.
Published 3 min readWrite a runbook someone else can follow
A runbook is useful when an operator can use it under pressure without already knowing the system’s hidden assumptions. It should connect a recognizable situation to a safe, observable response.
Published 3 min readMake routine maintenance visible in the delivery plan
Software continues to require work after its first release. Maintenance becomes easier to manage when the team can explain what is being maintained and which operational consequence the work addresses.
Published 3 min readTurn feature requests into problems you can solve
People usually request features in the language of the tools they know. The request is useful evidence, but it is worth understanding the situation that produced it before choosing an implementation.
Published 3 min readMake approval workflows explicit
An approval is a decision with an owner, a scope, and a consequence. A checkbox labeled “approved” rarely describes enough of the process to support real operational work.
Published 3 min readMake form submissions safe to retry
A browser can lose the response to a request that the server already processed. Retrying should help the person recover confidence without creating another order, inquiry, or job unintentionally.
Published 5 min readTest a backup by restoring the application
A backup file existing is only part of a recovery plan. The team also needs to know whether it can retrieve the right copy, restore the data, and use the application afterward.
Published 4 min readReview dependencies as operational commitments
Adding a dependency can save implementation work, but it also introduces something the team must understand, update, and operate around. Evaluate that commitment alongside the feature it provides.
Published 3 min readPrioritize a backlog without pretending every score is precise
A backlog is a list of possible work, not a promise to complete everything. Prioritization is most useful when it exposes the reasons behind a decision and the uncertainty around it.
Published 3 min readShow application status in language people can act on
A status label should help someone understand where work stands. Internal implementation terms can be accurate yet still leave the person unsure whether to wait, retry, or contact someone.
Published 3 min readDecide how incoming webhooks become application work
A webhook delivers information from another system, but delivery and business processing are separate concerns. Define that boundary before using incoming events to change important records.
Published 3 min readPrepare rollback before the release
A rollback plan should explain which changes can be reversed and which effects remain. Keeping the previous application image is useful, but it does not automatically reverse data or external actions.
Published 3 min readKeep configuration understandable across environments
Configuration determines how the same application behaves in different environments. Unrecorded values and unclear ownership can make a release difficult to reproduce even when the code is identical.
Published 3 min readWrite acceptance criteria around behavior
Acceptance criteria turn an abstract request into behavior that people can inspect. They should clarify the boundary of the work while leaving room for implementation decisions.
Published 5 min readDefine what search needs to find
Search is a collection of product decisions about matching, permissions, and presentation. Adding a search box before defining those decisions can produce results that are technically valid and operationally frustrating.
Published 3 min readReconcile systems instead of assuming synchronization worked
A synchronization process can report successful requests while the two systems still disagree. Reconciliation asks a different question: do the records now satisfy the relationship the workflow requires?
Published 3 min readInvestigate a slow application before choosing a fix
“Slow” can describe waiting for a page, submitting a form, receiving a background result, or navigating a large list. Start by identifying the experience before deciding which component to optimize.
Published 3 min readDefine roles around responsibilities
Roles should describe what people are responsible for, not simply reflect the buttons that happen to exist today. A clear model makes access easier to explain and review as the application grows.
Published 4 min readRun a discovery session that produces decisions
A discovery session should reduce a specific uncertainty. Bringing everyone into a room without a clear question often produces a long feature list and little shared understanding.
Published 3 min readGive each dashboard a decision to support
A dashboard becomes useful when it helps a particular person notice a condition and decide what to do. Displaying all available metrics often makes that task harder.
Published 3 min readDesign an API around a business operation
An API should give its callers a clear contract for accomplishing work. Exposing every internal table directly can make that contract difficult to evolve and easy to misuse.
Published 3 min readDesign background jobs as visible work
Moving a task into the background changes when it runs, not whether it needs a clear outcome. A job should have an identity, a lifecycle, and an operational path for failure.
Published 3 min readPlan the full lifecycle of a feature flag
A feature flag can separate deploying code from enabling behavior. It also creates an additional state the application must support, document, and eventually remove or retain deliberately.
Published 3 min readHandle scope changes without losing the original goal
New information will change a software project. The useful discipline is to make the effect of a change visible before the team quietly absorbs it into the work.
Published 3 min readWrite error messages that help people recover
An error message is part of the workflow. It should describe what the person needs to know, preserve their work where possible, and offer a next step the system can actually support.
Published 3 min readPlan for a supplier’s rate limits
An external service sets a boundary on how much work your application can request. The design needs to account for that boundary during normal operation, spikes, and recovery from an outage.
Published 3 min readWrite incident updates that support decisions
During an incident, people need to know what is affected, what they can do, and when they will hear more. A useful update separates observations from explanations that are still being investigated.
Published 3 min readBuild test data that represents awkward cases
Test data shapes what a team notices before release. A collection of clean, short, recently created records can make an interface or workflow appear more complete than it is.
Published 3 min readMake uncertainty visible in software estimates
An estimate is a planning tool built from what is currently known. Its usefulness depends on explaining the assumptions that could change it, rather than presenting uncertainty as an exact date.
Published 3 min readMake bulk actions inspectable and recoverable
A bulk action multiplies both useful work and mistakes. Its design should help people understand the selected records, the effect of the operation, and the outcome of each item.
Published 3 min readSeparate uploading a file from processing it
A successful upload means the bytes arrived, not necessarily that the file is usable. Model those stages separately so people understand what the system has accepted and what remains to happen.
Published 3 min readTurn an incident review into useful follow-up work
An incident review should explain how the system reached an undesirable state and what would make a similar event easier to prevent, detect, or recover from. A timeline alone is not enough.
Published 3 min readUse support requests as product evidence
Support conversations contain examples of where the application and the user’s expectations diverge. Organizing those examples can improve the product without turning every request into an immediate feature commitment.
Published 3 min readDecide what should stay manual
A task being repetitive does not automatically make it a good automation target. Some work changes too often, happens too rarely, or relies on judgment that has not yet been made explicit.
Published 3 min readTreat administration screens as part of the product
Administration screens shape how a system is supported and corrected. Leaving them until the end often turns routine operational work into ad hoc scripts and direct database edits.
Published 3 min readRehearse a data migration before the cutover
A migration rehearsal tests the procedure as well as the transformation. It should help the team understand the time required, the exceptions to resolve, and the evidence needed before switching users over.
Published 3 min readTrack email delivery beyond the send request
An email provider accepting a request does not guarantee that the recipient can read the message. Applications should distinguish the stages that matter to the workflow and expose failures appropriately.
Published 3 min readReview an existing application before proposing a rewrite
An existing application contains useful behavior, accumulated assumptions, and operational knowledge. A review should identify what is failing and what can be improved before treating replacement as the default answer.
Published 3 min read
Direct-style database calls
How compiler-hidden continuations let Gray wait for PostgreSQL without blocking the network reactor.
Published 3 min read
Measuring server performance
The HTTP qualification announcement, with methodology and limitations in the research archive.
Published 3 min read
The Gray commercial preview
The language, runtime, and scope of Gray’s commercial preview.
Published 3 min read
Native loops in Gray
The native-loop JIT announcement and the specific workload behind its reported result.
Published 3 min readTry a broader search.
Use fewer words, choose another topic, or remove the reading-time and format filters.