Most landing page forms are built by the person who needs the data, not the person filling it in. The result is a form shaped around your CRM rather than your visitor's patience. We recently trimmed a client's quote-request form from nine fields to four — name, email, a single dropdown, and a free-text box — and their completion rate went from roughly 18% to just over 40%. The missing fields weren't deleted; they moved into a follow-up email the sales team sent within the hour, when the lead was already warm.
Each field in a form is a micro-decision. "Do I trust them with my phone number?" "What do they mean by 'company size'?" "Is this worth finishing?" On mobile, every extra input also means a keyboard switch, a scroll, or an autocomplete misfire. The friction is cumulative but invisible if you only ever test the form on your own laptop with your own details already in your browser's autofill. Testing on a fresh device, with no saved data, on a middling 4G connection, gives you the experience your actual visitors have.
The practical move: audit every field against one question — do you need this before the first human reply, or after? Anything that can wait should wait. If you must keep a longer form, break it into two visible steps with a progress indicator so the user feels momentum rather than a wall. The goal of the form is not to capture a complete record. It is to start a conversation.
Your pull request template is empty and reviewers are guessing what they're supposed to look at
Most small teams we work with have code review turned on but no pull request template. The result is a diff with a title like 'fix stuff' or 'update checkout', and the reviewer opens it cold. They scroll through forty changed files trying to figure out what changed, why it changed, and what they should actually be testing. Half the time they skim it, approve it, and move on because the context gap is too wide to bridge on their own.
A good PR template doesn't need to be elaborate. Three prompts do most of the work: what does this change do, what's the riskiest part of the change, and how can the reviewer verify it works. That third one is the one people skip, and it's the one that matters most. If the author can't describe how to verify their own change, that's a signal the change isn't ready for review yet. We've caught more bugs from authors filling in that field than from reviewers reading the diff.
Set up a `.github/pull_request_template.md` or the equivalent in your Git host. Keep it to five lines or fewer — anything longer and developers will delete the template out of spite. The goal isn't bureaucracy, it's giving your reviewer a fighting chance to catch something meaningful before it ships. A two-minute investment from the author saves a fifteen-minute guessing game from the reviewer, and the review actually becomes useful instead of ceremonial.
Charging per seat made sense when you had ten users and now it punishes your best customers for growing
Per-seat pricing is the default for most B2B SaaS products because it is simple to explain and simple to bill. But if your product becomes genuinely useful inside a company, per-seat pricing starts to work against you. The operations manager who championed your tool internally now has to fight for budget every time a new team member needs access. Instead of your product spreading organically through the organisation, the pricing model turns every new user into a procurement conversation.
We have seen this play out with clients building workforce tools for South African businesses where team sizes fluctuate seasonally. A cleaning company with forty staff in December and fifteen in March does not want to manage licence counts monthly. A flat fee with a usage threshold, or a tiered band that covers a range, removes that friction entirely. The shift is not about charging less — it is about removing the moment where your customer has to actively decide whether adding another person is worth it.
If you are early-stage and already on per-seat pricing, look at your churn data by account size. If larger accounts churn faster or resist expansion, the model is likely the bottleneck. Consider a hybrid: charge per seat up to a cap, then flatten. Or price around a value metric that scales with outcomes, not headcount. The goal is to make growth inside a customer account feel effortless rather than penalised.
Lazy loading your images below the fold but eagerly loading the one above it is a one-line fix most sites still miss
Every modern browser supports the native `loading="lazy"` attribute on images, and most developers know to add it. The problem is they add it everywhere, including the hero image or banner that sits in the initial viewport. That image now waits for the browser's lazy-loading observer to notice it's visible before it starts fetching. On a fast connection you barely notice. On a 3G connection in Khayelitsha, that hero image pops in a full second or two late, and your Largest Contentful Paint score tanks because LCP almost always tracks the biggest visible element above the fold.
The fix is straightforward but requires you to think about each image individually rather than applying a blanket rule. Images above the fold — typically your hero, logo, and any prominent product shot — should have `loading="eager"` set explicitly, or simply omit the attribute entirely since eager is the default. Pair that with a `fetchpriority="high"` on your LCP image to tell the browser to prioritise it over other resources discovered at the same time. Everything below the fold keeps `loading="lazy"` as normal. If you use a framework like Next.js or Nuxt, their image components often handle priority hints, but only if you flag which image matters — they cannot guess your layout.
Audit one page at a time. Open DevTools, run a Lighthouse pass, and check which element is tagged as LCP. If that element has `loading="lazy"`, remove it and add `fetchpriority="high"`. Test on a throttled connection to confirm the improvement. It is a five-minute change that genuinely moves your Core Web Vitals score without touching your build pipeline.
Flutter's platform channel is the escape hatch nobody plans for until the Bluetooth library stops cooperating
Cross-platform frameworks sell you on a single codebase, and for screens, navigation, and API calls that promise mostly holds. Where it falls apart is hardware. The moment your Flutter app needs to talk to a Bluetooth peripheral — a card reader, a sensor, a printer — you discover that the community plugin either lags behind the latest Android SDK, handles iOS edge cases poorly, or simply doesn't expose the method you need. You're now writing platform-specific Swift or Kotlin anyway, funnelling it through a platform channel, and debugging serialisation mismatches between Dart and native land.
The mistake teams make is treating this as a surprise. If your product roadmap includes any hardware integration — NFC, biometrics beyond the basics, background location with fine accuracy — scope the platform channel work from day one. Build a thin native module early, write integration tests against it on both platforms, and keep the Dart side as a clean interface that doesn't leak platform assumptions. Trying to retrofit this after six months of pure-Dart development means restructuring your dependency injection and probably your state management layer too.
The practical takeaway: before you commit to Flutter (or any cross-platform tool) for a project, list every hardware touchpoint the app will need in its first year. If more than one requires native code, budget 20-30% of your dev time for platform channel work and native debugging. That budget isn't a failure of cross-platform — it's the honest cost of shipping on two operating systems. Knowing it upfront stops the timeline from quietly doubling midway through.
The scope document that says everything except what happens when you finish late
Most scope documents we review from other studios or freelancers cover features, milestones, and payment schedules in decent detail. Almost none of them say what actually happens when the timeline slips. Not a vague 'we'll communicate proactively' clause — a concrete description of who absorbs the cost of an extra two weeks, how priorities get re-sorted, and whether the client can trim scope to hold the date. That missing section is where most fixed-price relationships quietly fall apart.
We started adding a short 'overrun protocol' to every project agreement about two years ago. It's three or four sentences. It states that if a milestone is at risk of slipping by more than five business days, we flag it in writing, propose two options — cut a named feature to hold the date, or extend the date with a capped additional cost — and the client picks one within 48 hours. The specificity matters. It removes the awkward silence where both sides know something is late but nobody wants to name it, and it stops the creeping pattern where a project drifts three weeks over with no formal decision ever being made.
The key insight is that clients rarely object to this clause at signing. Most appreciate it because it tells them you've been late before and you've built a system for handling it honestly. That credibility is worth more than pretending every project lands on time. If you freelance or run a small studio, draft your overrun protocol this week and paste it into your next proposal. It takes ten minutes and saves the relationship conversation you'd rather not have at month four.
That spreadsheet your ops manager emails around every Monday is a database with no backup and no permissions
Most businesses we work with have at least one critical spreadsheet that quietly runs an entire process. Stock reorders, client onboarding checklists, commission calculations, delivery schedules — someone built it years ago, it lives on a shared drive or worse in someone's inbox, and the whole team treats it as the source of truth. The problem is not that spreadsheets are bad tools. The problem is that this one now has forty tabs, three people paste into it simultaneously, and nobody knows what breaks if a row gets deleted.
Replacing it does not require a six-month ERP rollout. What it usually needs is a small database behind a simple interface — something like a lightweight internal tool built on Retool, Budibase, or even a well-structured Airtable with proper field validation and role-based access. The key step most teams skip is mapping the actual workflow the spreadsheet encodes before choosing any tool. That spreadsheet is not just data; it is a set of business rules someone embedded in conditional formatting and hidden columns. If you migrate the data without extracting those rules, you rebuild the same mess in a shinier container.
Before you automate anything, sit with the person who maintains that spreadsheet for thirty minutes. Ask them what they check first, what they fix manually each week, and what they wish it could do that it cannot. That conversation is your requirements document, and it is more accurate than any brief written from memory. Extract the logic, then pick the tool.