Large context windows — 128k tokens and above — invite a specific kind of sloppiness. Instead of preparing the right information for an LLM prompt, teams dump entire codebases, full Slack threads, or raw database exports into the context and hope the model will sort it out. Sometimes it does. But the failure mode is quiet: the model returns something plausible-sounding that subtly misses the point because it prioritised the wrong section of your wall of text. You don't get an error. You get confident nonsense.
We've been building internal tools that call GPT-4o for summarisation and code review assistance, and the single biggest quality improvement came from sending less context, not more. Trimming a 40k-token prompt down to 6k — by extracting only the relevant diff, the ticket description, and the specific function signatures involved — cut our hallucination rate on code review suggestions noticeably. It also dropped latency and cost per call, because both scale with token count. The model isn't a search engine; it doesn't reliably find the needle in your haystack just because the haystack fits.
The practical discipline is simple: treat context preparation as a real engineering step, not an afterthought. Write a small function that selects and formats only the relevant inputs before the API call. If you're using RAG, check what your retrieval step actually returns — we've seen chunk sizes and overlap settings that feed the model three near-duplicate paragraphs while missing the one that matters. Smaller, well-chosen context consistently beats big, lazy context.
Your staging environment drifted from production three months ago and nobody noticed
Most small teams start with good intentions: a staging server that mirrors production so you can catch problems before they reach users. Then someone upgrades the database engine in production but not in staging. A new environment variable gets added to the deploy script but never to the staging config. The SSL cert on staging expires and someone just disables HTTPS checking in the test suite. Bit by bit, staging stops being a reliable proxy for production and starts being a place where green tests give you false confidence.
The dangerous part isn't the drift itself — it's that deployments keep working fine for weeks after the environments diverge. You only discover the gap when something subtle breaks: a query that behaves differently across Postgres versions, a service that relies on a header your staging load balancer doesn't forward, or a timezone default that silently changed. By then, debugging is expensive because the drift happened gradually and nobody logged it.
The fix isn't more discipline; it's automation. Treat your infrastructure config — Docker Compose files, Terraform definitions, environment variable templates — as code that gets diffed on every pull request. Run a weekly CI job that compares staging and production configurations and fails loudly when they diverge. If your staging environment can't be rebuilt from a script in under thirty minutes, it's not reproducible enough to trust. A staging server that quietly lies to you is worse than having no staging at all.
Design your onboarding flow for someone topping up 1 GB at a time
Most of the onboarding flows we admire — and copy — were designed for users on uncapped fibre or American office Wi-Fi. They pull in a 4 MB hero video, lazy-load three JavaScript bundles, and assume the person will sit through a five-step wizard without worrying about what it costs them in airtime. In South Africa, a significant share of your users are buying data in small bundles, often prepaid, often on a phone that throttles background connections aggressively. If your first-run experience burns through 15 MB before the user sees any value, you've spent their money for them and they'll remember that.
We rebuilt an onboarding flow for a local client earlier this year with a hard constraint: the entire first session — sign-up, one core action, confirmation — had to land under 2 MB transferred. That meant no auto-playing video, no analytics bundle on the first paint, no downloading a font family the system font could handle. We deferred the product tour until session two and replaced illustrative PNGs with inline SVGs that weighed almost nothing. The result was a measurably higher completion rate on mobile, particularly in areas outside major metros.
The practical takeaway: audit your onboarding with Chrome DevTools' network tab throttled to a slow 3G preset, and watch the transferred bytes. Set yourself a budget — 3 MB is generous, 1.5 MB is respectful — and treat every asset that pushes past it as a conversion risk. Your users in Limpopo or the Eastern Cape aren't on worse phones because they care less about your product. They just won't pay twice to find out what it does.
The trust signals above your fold are probably talking to yourself, not your customer
Most landing pages we audit have some version of the same trust cluster near the top: a row of client logos, a "trusted by 500+ companies" badge, maybe a security seal. The problem isn't that these are bad ideas. It's that they're chosen based on what makes the business feel credible, not what the visitor actually needs reassured about at that exact moment. A first-time visitor from a Google ad wondering whether your invoicing tool handles VAT correctly doesn't care that you've got an ISO badge. They care whether someone like them — a small accounting firm in Joburg — has actually used this thing and not regretted it.
The fix is embarrassingly simple: match your trust signals to the specific objection that lives next to each conversion point. If your sign-up form asks for a credit card, the reassurance right there should address payment fear — a cancellation policy line, not a generic star rating. If your pricing section is where visitors drop off (check your scroll-depth data), that's where a short testimonial about value-for-money belongs, not buried in a carousel at the bottom. Trust signals work hardest when they answer the doubt the visitor is having right now, not a general anxiety about your brand.
Audit your landing page section by section. For each call to action, write down the single most likely hesitation a visitor feels at that point. Then check whether any nearby element actually addresses it. If your trust signals are clustered in one spot doing the same job, redistribute them. Place them where doubt lives.
Your code review comments disappear the moment the PR merges and that knowledge walks out with the reviewer
Most teams treat pull request comments as ephemeral conversation. Someone explains why a particular approach won't scale, why that library has a subtle timezone bug, or why the database index matters for the query pattern downstream. The PR merges, the branch deletes, and those comments become archaeological artefacts buried three hundred entries deep in your Git host's search. When a new developer hits the same problem eight months later, they'll make the same mistake because the insight lived in a thread nobody will ever revisit.
The fix isn't heavier process — it's a lightweight habit. When a review comment explains a non-obvious decision, the reviewer or author should move that explanation into the code itself: an inline comment, an ADR (architecture decision record) in the repo, or a short note in the project's decision log. It takes about ninety seconds. The key distinction is between comments that say "rename this variable" (disposable) and comments that say "we tried event sourcing here and it broke under partial connectivity" (durable knowledge). Only the second kind needs rescuing.
We started tagging these moments in reviews with a simple label — "decision" — so it's obvious when something deserves promotion out of the PR thread. Within a few months, our decision log became the first place new joiners checked before proposing architectural changes. It doesn't require a wiki or a fancy tool. A markdown file in the repo root called DECISIONS.md, dated and appended to, works. The practical takeaway: next time you write a review comment longer than two sentences explaining why, stop and ask whether that reasoning belongs somewhere a future developer will actually find it.
Charge for your MVP before you build the second feature
Most founders we work with delay pricing until the product feels complete. They ship a login, a dashboard, three integrations, an export button, and a settings page — all before asking a single user to pay. By that point they've spent months building on assumptions instead of evidence. The first transaction is the most important validation event in a product's life, and it should happen embarrassingly early.
Putting up a payment wall forces a conversation that free access never will. A user who hits a price and leaves has told you something specific: either the value isn't clear, the amount is wrong, or they were never your customer. A user who pays R50 for a half-finished tool has told you something far more valuable — that the core problem is painful enough to spend money on right now. You cannot learn either of these things while everything is free. We've watched teams build for six months, launch with a pricing page, get silence, and realise they optimised the wrong workflow entirely.
This doesn't mean you need Stripe integration on day one. A manual invoice, a Google Form that collects card details, even a simple EFT request — the mechanism matters less than the act of asking. The goal is to find out whether your thing is a painkiller or a vitamin before you've built the pharmacy. If you're working on an MVP right now and nobody has paid you yet, make that your next milestone, not the next feature.
A single render-blocking Google Font request adds 400ms before your user sees anything
Most sites we audit in South Africa load Google Fonts with a standard <link> tag in the <head>. That triggers a chain: the browser fetches a small CSS file from fonts.googleapis.com, parses it, then makes a second request to fonts.gstatic.com for the actual font files. Until that chain resolves, text either stays invisible (FOIT) or flashes from a fallback to the web font (FOUT). On a fibre connection you barely notice. On mobile in Khayelitsha or Limpopo, those two sequential round-trips regularly add 300–500ms of blank content. That is time your visitor spends staring at nothing, and it tanks your Largest Contentful Paint score.
The fix is straightforward and takes about ten minutes. Download the font files, self-host them from your own domain or CDN, and load them with font-display: swap in a local @font-face declaration. You eliminate the extra DNS lookup to Google's servers, you cut one network hop out of the chain entirely, and you give the browser permission to show fallback text immediately while the font loads in the background. If you want to go further, subset the font to only the character ranges you actually use — most Latin-script sites can trim a 40kB file down to under 15kB with a tool like glyphhanger.
Takeaway: audit your waterfall in Chrome DevTools or WebPageTest. If you see requests to fonts.googleapis.com blocking your first paint, self-host those files this week. It is one of the highest-impact, lowest-effort performance wins available, especially for users on slower South African mobile connections.