Skip to content
Craig Jones

Type to search the site.

    Overview

    Shipping the whole product, not just the feature

    August 6, 2026
    3 min read

    A feature is not finished when it works on the developer’s machine. It is finished when the intended user can depend on it—and when the team can understand what happened if they cannot.

    That definition pulls deployment, observability and recovery into product engineering. They are not a separate layer of serious-looking work applied after the interesting part.

    Fail before release

    The SpicyBrain mobile application depends on a real deployment identity, native application identifiers, push credentials and an HTTPS production API.

    A release build should fail if those values are placeholders. Starting successfully and then discovering that notifications cannot register would be a worse outcome: the artefact would exist, but its core promise would already be broken.

    The same principle applies to insecure endpoints. Local development may need a LAN address so a physical phone can reach the backend. Production should reject plain HTTP rather than quietly sending authentication material over it.

    Tip (Move failure left)

    A clear release-time failure is cheaper than a mysterious runtime failure on somebody else’s device.

    Reproducibility reduces heroics

    Infrastructure and environment configuration should be inspectable and repeatable. The goal is not automation for its own sake. It is to avoid a production environment whose most important properties exist only in one person’s memory.

    For a small product, that means keeping the approach proportionate:

    • version the configuration that defines environments;
    • keep secrets out of source control;
    • make plans reviewable before they change infrastructure;
    • document the few manual steps that genuinely need a human;
    • know how to restore service before an incident demands it.

    The best operational process is usually the smallest one that can be repeated safely.

    Monitoring should answer a question

    Collecting logs and errors is easy. Knowing what they mean is harder.

    For reminder delivery, useful signals include queued work that is not progressing, provider failures, expired push subscriptions and repeated jobs. For a mobile release, useful signals include startup failures caused by configuration and crashes around notification handling.

    Each signal should connect to an action. A dashboard nobody checks and an alert nobody can interpret are both forms of unowned state.

    Recovery is a product flow

    Recovery includes more than restoring a database. A user may need to reconnect an integration, re-register a device token, sign in again or understand why a channel has disappeared from their defaults.

    The interface has to represent those states honestly. If the system knows a browser subscription has expired, leaving “browser push” selected as though it still works is not graceful degradation. It is misleading configuration.

    This is why operational state sometimes belongs in the product model. People need a path from failure back to a working setup.

    Private code can still produce public evidence

    Spicy Brain’s main repositories are private. That limits what I can show, but it does not prevent me from explaining how I think.

    I can describe the boundaries between server and client, the invariants around reminders, the reason release checks fail closed, and the trade-offs behind platform-specific notification handling. Those decisions are more useful evidence than a screenshot of a green contribution graph.

    Keeping the code private also creates a healthy constraint for this writing: explain the idea without relying on a giant pasted implementation.

    Ownership is the thread

    Across application code and operations, the recurring question is ownership:

    • Which system owns the durable value?
    • Which process is allowed to change it?
    • Which check prevents an unsafe release?
    • Which signal tells us the feature is unhealthy?
    • Who or what can recover it?

    When those answers are clear, a feature is easier to ship and less frightening to change.

    That is the kind of product engineering I want to keep doing: not just making the happy path exist, but making the whole thing dependable enough to deserve trust.