David Marko

Call Directions Website

David Marko PM, System Architect, Ustaz

Systems and Vendor Integrations: Expecting the WorstIntegrating third-party vendors accelerates development, but it intr...
29/06/2026

Systems and Vendor Integrations: Expecting the Worst

Integrating third-party vendors accelerates development, but it introduces a critical risk: you are outsourcing core functionality while retaining 100% of the accountability. When a vendor experiences an outage, your customers do not care whose infrastructure failed; they only know your application is broken. To build resilient architecture, you must design under a single assumption: the vendor will fail.

The Fallacy of the Flawless API
Vendor documentation often implies flawless uptime, but production environments inevitably expose the realities of distributed systems:
Transient Timeouts: A request that normally takes 200 milliseconds suddenly stretches to 30 seconds, exhausting your internal server threads.
Silent Breaking Changes: Unannounced modifications to payload structures or data types that bypass their tests but break your application's parsing logic.
Aggressive Throttling: Unexpected rate limiting applied to your account during peak traffic hours.

Architectural Defenses: Outbound Resilience
To prevent a third-party failure from causing a cascading system collapse, you must implement strict architectural firewalls:
Circuit Breakers: Monitor failure rates. If vendor errors spike past a threshold, the circuit "trips," immediately failing subsequent calls or routing to a fallback. This prevents your system resources from being consumed by a dying dependency.
Asynchronous Queueing: Avoid synchronous vendor API calls during critical user workflows. Push the event to a message queue and let background workers handle the integration out-of-band to keep the UI responsive.
Dead Letter Queues (DLQs): Use exponential backoff for failed asynchronous operations. When a message fails repeatedly after maximum retries, route it to a DLQ to isolate the payload for manual inspection.

Defending the Ingress: Webhook Security
When vendors send data back via webhooks, you are exposing endpoints to the public internet. These require strict defenses:
Strict Verification: Never assume a payload is legitimate. Enforce cryptographic signature verification to confirm the data originated from the trusted vendor and was not tampered with.
Immediate Acknowledgement: Webhook handlers must do one thing: receive the payload, validate it, push it to an internal queue, and return an HTTP 200 status instantly. Never execute heavy business logic while holding the vendor's connection open.
Enforce Idempotency: Vendors will inevitably send duplicate webhooks due to network retries. Track processed webhook IDs to ensure the same event is never executed twice.

Designing for Graceful Degradation
Ultimately, your architecture must account for the complete disappearance of a vendor. If an analytics provider or payment gateway goes offline, users should still be able to navigate the core application.

Documentation: Do You read(me.md)?Writing robust code is only half the engineering equation. Without comprehensive docum...
26/06/2026

Documentation: Do You read(me.md)?

Writing robust code is only half the engineering equation. Without comprehensive documentation, even the most sophisticated systems become inaccessible liabilities. Documentation serves as the primary interface for your codebase. It systematically reduces redundant support inquiries, accelerates team onboarding, and is often the defining factor in whether a platform is successfully adopted or ultimately abandoned.
Effective documentation requires a strategic approach, particularly when addressing the distinctly different needs of internal engineers and external customers.

The Audience:
Documentation cannot be a monolith; it must be tailored to the reader's objectives.

Technical Documentation for Engineers: When writing for other developers or internal teams, the focus must be on architecture, system constraints, and local setup. Engineers need to understand how the system is structured, where the data flows, and how to contribute without breaking existing paradigms. This documentation lives close to the code, often residing directly in the repository.

Product Documentation for Customers: External users and integrators do not need to understand your internal system architecture. They need clear use cases, integration tutorials, and outcome-driven guides. Product documentation must prioritize clarity, time-to-value, and reliable API references that help the customer achieve their specific business goals.

Markdown:

The foundation of modern documentation is Markdown. It has become the industry standard because it perfectly balances human readability with programmatic flexibility.
Markdown syntax allows developers to format text using simple, standard characters without writing heavy HTML. Using hashes for headers, backticks for inline code, triple backticks for syntax-highlighted code blocks, and brackets for links provides a structured way to convey technical thought. Because it is plain text, Markdown is inherently version-controllable, allowing documentation to undergo the same rigorous pull-request and review processes as the application code itself.

The Power of OpenAPI Specifications
When documenting APIs, relying on static, hand-written files is a critical operational error. Hand-written API documentation inevitably drifts out of sync with the underlying codebase.
OpenAPI specifications solve this by providing a strict, machine-readable contract for your API. Maintaining an accurate OpenAPI spec shifts documentation from a manual chore to an automated asset. It unlocks an entire ecosystem of technical tooling, allowing teams to automatically generate interactive API playgrounds, scaffold client-side SDKs, and keep frontend TypeScript definitions perfectly aligned with backend responses.

The Modern Documentation Stack
Teams no longer need to build custom documentation sites from scratch or maintain complex static site generators. The current ecosystem offers highly optimized frameworks designed to render Markdown and OpenAPI specs into professional web experiences.
Starlight by Astro: An excellent choice for teams prioritizing performance and simplicity. Built on top of the Astro framework, Starlight focuses heavily on Markdown and ships with zero JavaScript by default. It is highly suited for open-source projects or extensive technical repositories where you need a clean, highly performant site with minimal initial configuration.
Fumadocs: For engineering teams heavily invested in the Next.js ecosystem, Fumadocs is an ideal solution. Built entirely around the Next.js App Router and React Server Components, it offers a CLI-driven approach. Instead of forcing you into a rigid, pre-built theme, it allows you to install core UI components directly into your project, giving you total architectural control over the layout and design.
Mintlify: When the goal is delivering premium, enterprise-grade product documentation without allocating engineering resources to maintain the infrastructure, Mintlify is a strong candidate. It is a managed platform that continuously syncs with your Git repository. It natively handles MDX, effortlessly renders OpenAPI specs into interactive playgrounds, and includes built-in analytics and search, allowing your team to focus strictly on content creation.

By treating documentation with the same rigorous architectural standards as your code, you ensure that your software is not just functional, but genuinely usable.

One Star Reviews and 2G Networks: Building for Emerging MarketsBuilding software for emerging markets sounds incredibly ...
25/06/2026

One Star Reviews and 2G Networks: Building for Emerging Markets

Building software for emerging markets sounds incredibly noble in a pitch deck. You are connecting the unconnected. You are building for the next billion users.
Then you actually deploy the app, and the reality of local infrastructure hits you like a brick.
Building for emerging markets means designing for inexpensive Android phones with 2GB of RAM and processors that struggle to open the system keyboard. It means dealing with networks that drop the moment someone walks past a microwave. It also means dealing with internet data costs so high that users physically turn off their cellular data between messaging apps to ration their megabytes.

The most soul crushing part of this reality is the app store reviews. You can write the cleanest, most performant code of your career. You can optimize your rendering cycles until you are blue in the face.
And you will still wake up to a One star review that says: "App is useless. Stuck on loading screen forever."
It is not stuck, my friend. You are trying to download a payload on a fluctuating Edge network while sitting on a moving bus in a dead zone. But the user does not care about cellular packet loss or DNS resolution failures. They only see that the spinner is spinning, and they blame your app. You cannot reply and explain TCP handshakes to them. You just have to take the hit.

To survive this, you have to build defensively. You have to assume the network is actively trying to sabotage you.

Backend Defensive Driving
Your backend cannot just assume a pristine fiber-optic connection.

Payload Trimming: If you are sending massive JSON objects with fifty fields when the UI only needs three, you are literally stealing the user's expensive data plan. Keep payloads ruthlessly small and paginate everything.

Idempotency is Non Negotiable: Requests will time out. The user's phone will drop the connection right after hitting submit, and they will mash the "Checkout" button four times in frustration. If your backend does not use idempotency keys to recognize the retries, you are going to charge that user four times for the same transaction.

Mobile: The Offline-First Mandate
Mobile development in these markets is an exercise in extreme constraints.

The Bundle Diet: If your mobile app is 150MB, nobody is downloading it. Users simply will not spend their precious data quota on your bloated bundle. Keep it under 20MB, or you do not exist.

Aggressive Caching: The app must work when the internet does not. Use local databases to cache everything instantly. If a user opens the app offline, they should see the last known state immediately, not a blank white screen. Let them queue actions offline and sync them silently in the background when the device finally catches a whiff of a 3G signal.

You will never be able to fix the local telecom infrastructure. But with a little empathy and a lot of caching, you can build systems smart enough to survive it.

Deployment Checklists: Surviving the Push to ProductionPushing code to production is either a completely boring non even...
24/06/2026

Deployment Checklists: Surviving the Push to Production

Pushing code to production is either a completely boring non event or a heart-pounding adrenaline rush. Relying on memory when deploying a major feature is a guaranteed recipe for an outage.
Even with sophisticated CI/CD pipelines doing the heavy lifting, a solid checklist ensures the human element does not drop the ball. Here is a battle-tested approach to deployments.

PreDeployment: Are We Actually Ready?

The Environment Variable Check: The silent killer of deployments. Are the production API keys set correctly? No leftover staging credentials?

Database Backups: Never run a schema migration without a fresh backup. You always need a guaranteed recovery point.

CI/CD Green Lights: Ensure every single automated test has passed. No bypassed warnings or "we will fix it later" excuses.

The Deployment Phase: Holding Your Breath

Run Migrations First: Database changes usually need to happen before the new application code goes live.

Traffic Routing: Watch your load balancers during rolling updates to ensure traffic transitions smoothly without dropping requests.

Tail the Logs: Keep a terminal window open streaming the live production logs. You are looking for immediate stack traces or timeout warnings.

PostDeployment: Did We Break Anything?

The Sanity Check: Open an incognito browser window and manually test the critical paths. Log in, load the dashboard, submit a form.

Error Tracking: Watch your monitoring tools for a sudden spike in new exceptions. Users will find edge cases in five minutes that QA missed for five weeks.

Performance Monitoring: Check database CPU usage and API response times. Missing indexes only become obvious under real traffic.

The Rollback Plan: If the worst happens, do not try to hotfix a broken application live. Hit the rollback button, revert to stable, and debug locally.

A good checklist is not about slowing down development. It is about removing the anxiety from shipping software, so you can deploy on a Friday afternoon and still sleep soundly that night.

System Architecture: Problems, Solutions, and New Problems from the SolutionsSystem architecture always starts the same ...
23/06/2026

System Architecture: Problems, Solutions, and New Problems from the Solutions

System architecture always starts the same way: a clean whiteboard, a handful of markers, and a whole lot of unearned optimism.
When you first map out a system, it looks pristine. Data flows perfectly from one neatly drawn box to another. But anyone who has built software knows that those code lines are hiding a tangled mess of tradeoffs. Architecture is rarely about finding the perfect answer; it is usually about choosing which headache you would rather deal with.

Here is a look at how the architectural cycle actually plays out.

The Steps and the Optimistic Assumptions

The standard architectural process usually goes something like this: define the requirements, design the data models, pick the tech stack, map the services, and start building.
During the design phase, we all suffer from the exact same optimistic assumptions. We assume the network will never fail. We assume the database will always respond in under ten milliseconds. We assume the third-party API we rely on will not randomly change its rate limits on a Friday afternoon.

We draw a straight line between the frontend and the backend and assume it represents a reliable, instantaneous connection. Spoiler alert: it never does.

How Solutions Become New Problems

This is the great irony of system architecture. Every time you implement a solution to fix a bottleneck, you almost always introduce a brand new, slightly more complicated problem.

The Problem: The database is getting hammered and reads are too slow.
The Solution: Introduce Redis as a caching layer to serve data faster.
The New Problem: Cache invalidation. Now you have to figure out how to keep the cache updated so users aren't looking at stale data, which introduces race conditions and logic bugs.

The Problem: The monolithic application is too massive, making deployments slow and team coordination a nightmare.
The Solution: Break the monolith into microservices so teams can ship independently.
The New Problem: Distributed systems complexity. Now you have to deal with network latency, complex API gateways, distributed tracing, and the nightmare of a single transaction failing halfway through a chain of five different services.

You never truly eliminate complexity in a system. You just move it around.

A Thousand Decisions: The Balancing Act

Ultimately, architecture is just an endless series of micro decisions where you are forced to weigh competing priorities.
Every time you draw a new box on the whiteboard, you are pulling levers on four main constraints:

Cost: Serverless functions might scale infinitely and solve your traffic spikes, but your AWS bill might suddenly look like a mortgage payment.

Time: Building a custom, perfectly optimized event-driven architecture is great, but if it takes six months and the startup runs out of funding in four, it is a failed architecture. Sometimes a boring, fast-to-ship solution wins.

Complexity: You can build for massive scale using Kubernetes, message queues, and a multi-region database setup. But if you only have a hundred daily active users, you just built a spaceship to go to the grocery store.

Skill Levels: This is the most overlooked constraint. You can design the most brilliant, cutting-edge system in the world. However, if your current engineering team does not have the skills to build, maintain, and debug it at 2:00 AM, it is the wrong architecture.

At the end of the day, good architecture is not about avoiding problems entirely. It is about understanding the cost of your solutions and consciously choosing the problems you and your team are willing to live with.

Frameworks that Speak my LanguageChoosing a programming language is only half the battle. The real magic happens when yo...
22/06/2026

Frameworks that Speak my Language

Choosing a programming language is only half the battle. The real magic happens when you pair that language with the right framework or tool to do the heavy lifting. Here is a look at my preferred languages and the ecosystem I build around them.

The Core Languages

TypeScript: It gives me the flexibility of JavaScript but with the safety net of static typing. It is the backbone of most of my web applications.

Python: Clean, readable, and incredibly versatile. Whether I am building backend services, scripting, or handling data, Python always delivers.

Go: When I need raw performance, concurrency, and fast compilation times, Go is exactly what I reach for. It is efficient, minimal, and predictable.

React: While technically a library, it completely shapes how I think about frontend architecture. It is my default mental model for building user interfaces.

YAML: Hear me out—this isn’t a language, but I spend too much time writing configs. Between CI/CD pipelines, Docker setup, and cloud infrastructure, it takes up enough of my daily workflow that it practically feels like one.

Favorite Frameworks and Tooling
This is the software ecosystem that helps me move fast and keep my codebases manageable:

Next.js and Express.js: My dual-threat for web development. Express handles lightweight APIs perfectly, while Next.js is my go-to for full-stack, production-ready React applications.

Django: When building Python backends that need a robust, batteries-included structure, Django saves me from reinventing the wheel.

React Router V7: The latest iteration keeps routing clean and powerful, bringing excellent data-loading capabilities directly into the UI layer.

Expo: Making cross-platform mobile development with React actually enjoyable. It abstracts away the usual headaches of native mobile builds.

Starlight by Astro: My absolute favorite framework for documentation sites. It is fast, Markdown-centered, and beautiful right out of the box.

Turborepo and Turbo: Essential for managing monorepos. They make build times lightning-fast with intelligent caching, meaning I spend significantly less time waiting for code to compile.

Kysely: Instead of relying on a heavy, bloated ORM, I use Kysely for database interactions. It provides type-safe SQL query building for TypeScript without losing control over the actual database queries.

Address

Nairobi South

Alerts

Be the first to know and let us send you an email when David Marko posts news and promotions. Your email address will not be used for any other purpose, and you can unsubscribe at any time.

Shortcuts

Share