The Software Development Process
A practical, educational guide from idea to GA
Introduction
High-quality software delivered fast is not an accident; it’s the result of a repeatable process that starts with the business domain, flows through clear boundaries and contracts, and ships in vertical slices with built-in quality. This article lays out that process end to end—what to do, why it matters, and how to keep it lean.
The Lifecycle at a Glance
- Discover & Frame the Domain
- Boundaries & Context Map
- Capabilities & Use Cases
- Domain Model (Entities/VO/Policies/Events)
- Application Layer (Ports/Use Cases)
- Adapters (HTTP/DB/Messaging)
- Vertical Slice Build
- Quality (Tests/Obs/SLOs)
- Security & Data
- Release (Flags/Canary)
- Docs & Governance
- Operate & Evolve
1) Discover & Frame the Domain
Goal: Align the team’s language and outcomes with the business reality.
- Ubiquitous Language: Define core terms with stakeholders; use them in code and docs.
- Problem & Outcomes: What pain are we removing? Which measurable results matter (KRs/NSM)?
- Non-Goals: What we won’t do (prevents scope creep).
Template — Domain Brief (≤1 page)
Domain: <name>
Problem/Outcome: <who is helped + measurable change>
Ubiquitous Language: <5–10 key terms with crisp definitions>
Bounded Contexts (initial guess): <1–3>
Key Business Events: <3–7 named in past tense>
Non-Goals: <out-of-scope items>
2) Boundaries & Context Map
Goal: Prevent one giant, inconsistent model.
- Bounded Contexts: Each context owns its model and invariants.
- Relationships: Upstream/Downstream, Anti-Corruption Layers, published events, consumed events.
- Contracts over Tables: Integration via events/HTTP/gRPC, not shared DBs.
Deliverables: a simple Context Map diagram and a list of event/API contracts.
3) Capabilities & Use Cases
Goal: Turn strategy into executable units.
- Capability Map: 5–10 capabilities per context (e.g., “Issue Invoice”).
- Prioritize: Impact vs. effort; choose one vertical slice first.
- Use Case (≤1 page): Primary flow (3–7 steps), business rules, errors, emitted events, Definition of Done (flag, logs, metric, contract test, rollback).
Template — Use Case
Title:
Actor:
Primary Flow: 1) ... 2) ... 3) ...
Business Rules: - ...
Errors/Exceptions: - ...
Emitted Events: - ...
DoD: flag + logs/metric/trace + contract tests + rollback plan
4) Domain Model (Entities, Value Objects, Policies, Events)
Goal: Encode business rules where they belong.
- Entities: Have identity; enforce invariants.
- Value Objects: Immutable, equality by value (e.g., Money, Email).
- Aggregates: Transactional consistency boundaries; keep them small.
- Policies: Cross-aggregate rules (e.g., “Only DRAFT can be issued”).
- Domain Events: Past-tense facts produced by aggregates.
Micro-Example (TypeScript)
export class Money {
  constructor(readonly value: number, readonly currency: "USD" | "BRL") {
    if (value < 0) throw new Error("Negative amount");
  }
}
export class InvoiceIssued {
  readonly type = "InvoiceIssued";
  constructor(public id: string, public total: Money) {}
}
export class Invoice {
  constructor(
    private status: "DRAFT" | "ISSUED" | "PAID",
    private total: Money
  ) {}
  issue() {
    if (this.status !== "DRAFT") throw new Error("Only DRAFT can be issued");
    this.status = "ISSUED";
    return new InvoiceIssued(crypto.randomUUID(), this.total);
  }
}
5) Application Layer (Ports & Use Cases)
Goal: Orchestrate domain logic; no infrastructure here.
- Ports: Interfaces for persistence, clock, event bus, external APIs.
- Use Case: Constructs aggregates/VOs, applies policies, commits via ports, publishes events.
- DTOs: Input/output types for boundaries (don’t leak entities).
export interface InvoiceRepo {
  save(inv: Invoice): Promise<void>;
  findById(id: string): Promise<Invoice | null>;
}
export interface EventBus {
  publish(event: object): Promise<void>;
}
export class IssueInvoice {
  constructor(private repo: InvoiceRepo, private bus: EventBus) {}
  async exec(input: { amount: number; currency: "USD" | "BRL" }) {
    const inv = new Invoice("DRAFT", new Money(input.amount, input.currency));
    const evt = inv.issue();
    await this.repo.save(inv);
    await this.bus.publish(evt);
    return { status: "ISSUED" };
  }
}
6) Adapters (Infrastructure at the Edges)
Goal: Connect to the outside world, keeping the domain pure.
- HTTP/gRPC Controllers: Validate input, call use case, map errors to status codes.
- Persistence Adapter (ORM/Driver): Map aggregates ↔ relational/NoSQL schema.
- Messaging Adapter: Publish/consume domain and integration events.
- Feature Flags: Wrap routes/paths to decouple deploy from release.
7) Build the First Vertical Slice
Goal: Deliver one end-to-end path with quality.
- Implement one use case through controller → use case → repo → bus.
- Add telemetry (logs/metrics/traces) and a flag.
- Ship behind a canary or internal ring; collect early signals.
Minimal folder layout
domains/<context>/
  domain/           # entities, VOs, policies, events
  application/      # ports, use cases, DTOs
  tests/            # unit + contract
apps/api/
  adapters/http/    # routes/controllers
  adapters/persistence/
  adapters/messaging/
infra/
  feature-flags/
  observability/dashboards/
8) Quality Gates (Tests, Observability, SLOs)
Goal: Make quality automatic.
- Testing Pyramid:
- Unit (domain invariants, policies).
- Contract (HTTP/event schemas, error mapping).
- Integration (DB, broker) where risk is highest.
- E2E (happy path only).
 
- Observability:
- Logs (structured, with business IDs).
- Metrics (SLIs: success rate, p95 latency).
- Traces (handler → use case → repo).
 
- SLOs & Error Budgets: Choose targets (e.g., p95 ≤ 200ms, 99.9% success/30d) and wire alerts.
Template — DoD (paste in PR)
- [ ] Feature flag in place and off by default
- [ ] Unit + contract tests passing in CI
- [ ] Logs, metric, and trace present
- [ ] Runbook attached; rollback tested
- [ ] SLOs defined and dashboard linked
9) Security, Privacy, and Threat Modeling
Goal: “Secure by default” from day one.
- Least Privilege & Zero Trust: Short-lived tokens, scoped permissions.
- Input Validation/Sanitization: Prevent injections and deserialization bugs.
- Secrets Management: Vault/KMS; no secrets in code or CI logs.
- Threat Modeling (STRIDE-lite): Identify spoofing/tampering/data exposure at the use-case boundary.
- Audit Trails: Immutable logs for sensitive actions.
10) Data Management & Migrations
Goal: Keep data safe and evolvable.
- Schema Evolution: Backward-compatible changes; blue/green migrations.
- Transactions & Sagas: Local ACID inside a service; sagas for cross-service flows.
- Retention & Residency: Policies by data class; encrypt at rest; minimize PII.
- Read Models: For CQRS, design fast, denormalized projections with rebuild strategy.
11) Release Strategy (Flags, Rings, Canary)
Goal: Reversible releases with fast feedback.
- Flags: Decouple deploy from release; enable targeted exposure.
- Rings/Canary: Staff → 5–10% → 50% → 100%, with automated rollback on SLO breach.
- Post-Release Checks: Error rates, latency, user behavior, business events.
12) Docs & Governance (Lightweight)
Goal: Decisions are discoverable; onboarding is fast.
- ADRs: One-pager per important decision (context → decision → consequences).
- README per Domain/Service: What it does, how to run, contracts, SLOs, runbook.
- Decision Log & Templates: Make the paved road the easiest road.
Template — ADR (≤1 page)
Title:
Context:
Decision:
Alternatives considered:
Consequences (positive/negative):
Date & Owners:
13) Operate & Evolve
Goal: Learn continuously and keep change cheap.
- Outcome Review (Weekly): KR deltas per capability; decide Scale / Iterate / Kill.
- Error Budget Policy: Slow down when reliability slips; invest in resilience.
- Evolutionary Architecture: Prefer reversible decisions; refactor toward modularity or microservices when team/coupling demands it.
Common Pitfalls & Antidotes
- Big Design Up Front: Antidote—one vertical slice first.
- Anemic Domain Model: Antidote—put rules in entities/VOs; test invariants.
- Leaky Boundaries: Antidote—ports/adapters; contract tests; no shared DB.
- Hidden Ops Debt: Antidote—SLOs, dashboards, runbooks from the first slice.
- Security as a phase: Antidote—threat modeling and least privilege in step 1.
48-Hour Starter Plan (Reality-Proof)
Day 1: Domain Brief + Context Map (90m), pick one capability, write Use Case (60m), model entities/VO/policy (2h).
Day 2: Implement use case + ports + adapters (3–4h), unit & contract tests (2h), logs/metric/trace + flag + runbook + SLO (2h), canary deploy (1h).
Conclusion
Starting from the domain grounds your system in business reality; moving through bounded contexts, contracts, and vertical slices keeps you fast and safe. The non-negotiables—tests, observability, SLOs, security, feature flags—turn quality into a property of the process, not heroics. Keep artifacts short, automate the checks, and iterate in small, meaningful steps. That’s how you ship high-quality software—quickly and repeatedly.