Engineering · Decisions

Architecture decision records.

By Mokshify Engineering · Reviewed by the team · Updated 17 Jul 2026 · 8 min read

Every technology in our stack got there through a decision we can defend. These are the records - context, decision, the why, and the tradeoffs we knowingly accepted. Each one lists what would make us revisit it, because a decision without an exit condition is a dogma.

ADR-001 · Flutter for mobile

Context: clients need Android and iOS; two native codebases double every feature. Decision: Flutter by default. Why: one codebase, both stores, near-native performance, fully custom UI - and one team owning the whole surface. Tradeoffs accepted: a Dart ecosystem smaller than JS; occasional platform-channel work for deep OS features. Revisit when: an app’s core depends on platform-exclusive capabilities - then we recommend native and say so in the proposal.

ADR-002 · FastAPI for Python services

Context: AI work lives in Python; APIs need typed contracts. Decision: FastAPI. Why: Pydantic validation at every boundary (half of API security for free), OpenAPI docs generated not written, async-first, fast enough. Tradeoffs: younger than Django - fewer batteries; we assemble auth and admin per product. Revisit when: a product is 90% CRUD admin - Django’s batteries can win there.

ADR-003 · PostgreSQL as the source of truth

Context: every product needs a database it will not outgrow in ways that hurt. Decision: PostgreSQL, always the default. Why: relational rigour, row-level security for tenancy, JSONB when shape flexibility is real, pgvector for AI retrieval - one engine, deeply known, covering 95% of data needs. Tradeoffs: not a warehouse, not a graph DB; extreme write volumes need care. Revisit when: analytics outgrow it (add a warehouse beside it - never replace it casually).

ADR-004 · Redis for the hot path

Context: caching, sessions, rate limits and queues all need sub-millisecond state. Decision: Redis for all four. Why: one operationally boring service instead of four exotic ones; data structures that match real needs. Tradeoffs: weaker queue semantics than a broker; memory is the budget. Revisit when: event streams need replay and fan-out - that is a broker’s job.

ADR-005 · Docker everywhere, Kubernetes when earned

Context: environments must match; scaling needs vary wildly by product. Decision: every workload ships in Docker; Kubernetes only when service count and scaling granularity justify it. Why: containers make laptop-equals-production true; Kubernetes is powerful and expensive to operate - premature adoption spends the innovation budget on plumbing. Tradeoffs: migrating a grown product onto K8s later is real work - we accept it, planned. Revisit when: many services, autoscaling needs, or multi-environment fleets arrive.

ADR-006 · Terraform for infrastructure

Context: four clouds (AWS, Azure, OCI, GCP), reproducible environments, reviewable changes. Decision: all infrastructure as Terraform. Why: one language across clouds, plan-before-apply, configuration that is documentation. Tradeoffs: state management discipline; occasional provider lag behind new cloud features. Revisit when: a team is all-in on one cloud with mature native IaC and no portability need.

ADR-007 · GitHub Actions for CI/CD

Context: code lives on GitHub; pipelines should live beside it. Decision: GitHub Actions runs the whole release pipeline. Why: zero extra infrastructure, reviewable YAML beside the code, marketplace depth, and our AI-review step drops in cleanly. Tradeoffs: vendor coupling; heavy monorepo builds need self-hosted runners eventually. Revisit when: build minutes or compliance demand self-hosted CI.

ADR-008 · Multi-provider AI (OpenAI + Claude + Gemini)

Context: model capability, price and policy shift quarterly. Decision: integrate all three behind one internal interface; route per use case. Why: reasoning depth, context length, latency and cost genuinely differ - and provider lock-in in 2026 is buying a single stock with the whole portfolio. Tradeoffs: an abstraction layer to maintain; evaluation suites must run per provider. Revisit when: a product’s needs collapse to one provider’s unique strength - then commit knowingly.

ADR-009 · Nginx at the gateway

Context: TLS, routing, rate limits and static delivery need a front door. Decision: Nginx. Why: two decades of production hardening, tiny footprint, does exactly what a product gateway needs. Tradeoffs: service-mesh features are not its job. Revisit when: the architecture genuinely becomes many-service with mesh-grade needs.

ADR-010 · Boring technology as policy

Context: every novel tool costs an innovation token; products have few to spend. Decision: spend novelty on the product, not the plumbing. Why: the stack above is deliberately unexciting - well-documented, hireable, operable at 3am. Clients get velocity and sleep. Tradeoffs: we occasionally adopt genuinely better tools a year late; we are content with that. Revisit when: a boring choice becomes a measured bottleneck - benchmarks first, migration second.


Related: The stack with reasons · Architecture gallery · Architecture library · Engineering journal