— Writing
Blog
Deep dives, tutorials, and real-world breakdowns on Python, automation, and software dev.
43 posts
TypeScript Generics for Testers: Writing One Helper Instead of Five
Most testers learn just enough TypeScript to get by, and generics are usually the first thing skipped. They're also the exact tool that turns five nearly-identical helper functions into one that actually stays type-safe.
The 404 That Broke Three Test Frameworks
A navigation pattern that looked completely normal was quietly broken in Playwright, WebdriverIO, and my own tutorial content — and it took a real HTTP request, not a stack trace, to find it.
The Testing Pyramid vs. the Testing Trophy: Two Real Strategies, Not One Right Answer
Mike Cohn's Testing Pyramid and Kent C. Dodds's Testing Trophy disagree about where most of your testing effort should go, and both have a real, specific argument for their answer. The mistake is treating either one as a universal law instead of a strategy that fits a particular kind of application.
What 'Test Isolation' Actually Means, and the Vocabulary for Getting It Right
'Tests should be isolated' is advice everyone repeats and almost nobody defines precisely. Gerard Meszaros's xUnit Test Patterns gives it exact vocabulary — Fresh Fixture, Shared Fixture, Erratic Test — that turns a vague virtue into a concrete design decision.
Mocks, Stubs, and Fakes: The Test Double Vocabulary Almost Everyone Gets Wrong
Nearly everyone says 'mock' when they mean 'stub.' The distinction sounds pedantic until you notice it's actually describing two fundamentally different ways of verifying that code did the right thing.
SOLID Principles, Applied to Test Automation Code
SOLID gets taught as application-architecture advice and almost never applied to the test code sitting right next to that architecture — even though test suites rot for exactly the same five reasons production code does.
Race Conditions: A Mental Model That Actually Explains Your Flaky Tests
'Race condition' gets used as a catch-all excuse for any flaky test nobody's bothered to diagnose. The actual definition is precise, comes from concurrent systems theory, and — once you know it properly — tells you exactly where to look.
Pure Functions and Why They're Easier to Test (a Precise Explanation, Not a Slogan)
'Pure functions are easier to test' is repeated constantly and rarely explained. The actual reason is a specific property called referential transparency, and once you can spot it, you can also spot exactly which parts of your codebase resist testing and why.
"Program to an Interface, Not an Implementation" — What That Actually Means for Test Code
It's the first principle in the Gang of Four's Design Patterns book, printed in italics as the foundational rule everything else in that book builds on. Most people who've heard the phrase can't explain what an 'interface' means here, or why it matters outside application architecture.
The Locator Hierarchy, From First Principles
Every framework's docs tell you to prefer role-based selectors over CSS classes. Almost none of them explain why, in a way that actually transfers when you hit a case the cheat sheet didn't cover. Here's the reasoning underneath the rule.
Immutability and the Aliasing Bug That Only Shows Up Three Functions Later
The most confusing bugs to debug aren't the ones that crash loudly — they're the ones where a value changed and nothing in the stack trace tells you where. Immutability doesn't eliminate bugs in general. It eliminates this one specific, disproportionately confusing category of them.
Idempotency: Why Your Tests (and the APIs They Call) Should Survive Running Twice
Idempotency is a real, precisely-defined engineering concept — not a synonym for 'safe' — and it explains both why your CI retries can silently create duplicate data and why a well-designed API makes that impossible in the first place.
I Said Allure 'Works' in Three Tutorials. Then I Actually Ran It.
Every reporting chapter on this site described Allure setup that had never once been run. Actually running it found a plugin that silently crashes Cypress 16, a Java dependency nobody mentions until it fails, and a flaky test that only a real repeated run would have caught.
DRY in Test Code: What the Principle Actually Says, and Where It Backfires
Andy Hunt and Dave Thomas's actual definition of DRY is narrower and more useful than the 'never repeat code' slogan it got flattened into — and test suites are exactly where the flattened version causes the most damage.
Design Patterns Beyond Page Object Model: Factory and Strategy
Page Object Model gets all the attention in testing tutorials, but it's one pattern out of dozens of well-established solutions to well-established problems. Two more — Factory and Strategy — solve problems every growing test suite eventually hits.
Dependency Injection: Why Playwright's Fixtures Exist, and How to Design Your Own
Playwright's fixture system feels like a testing-specific convenience until you recognize it as dependency injection wearing a test framework's clothes — a pattern with a name, a history, and rules that explain why fixtures behave the way they do.
Classes, Objects, and Why Page Objects Are Just OOP
Most test automation tutorials teach Page Object Model as a testing pattern with its own special rules. It isn't one — it's four ordinary object-oriented programming concepts, applied to a browser. Once you see that, half the 'best practices' debates around it stop being mysterious.
Async/Await and the Event Loop: What's Actually Happening When You Write await page.click()
Every test you've ever written is full of await. Most people using it correctly still can't explain what it actually does to their code at runtime — which is fine, until you hit a bug that only makes sense once you can.
Seven Merged PRs: My Real WebdriverIO Open Source Footprint
A full, honest accounting of everything I have actually shipped upstream to the WebdriverIO org — one feature, four docs fixes, one bug fix, one config cleanup — and what each one actually took.
What Mentoring Junior Engineers on Test Frameworks Actually Taught Me
Mentoring teammates on a framework I built taught me more about its design flaws than a year of writing tests in it myself.
Page Object Model Is Still Right (Just Not the Way Most Tutorials Teach It)
The common complaints about POM are real — but they're usually complaints about three specific anti-patterns, not about POM itself.
JSON Diffing Is Harder Than It Looks: Building a Comparison Tool
What started as "just diff two JSON strings" turned into a small tree-walking algorithm with more edge cases than I expected.
Django Channels and WebSockets: Real-Time Features in a Full-Stack App
Adding live task updates to Task Nest meant admitting that plain Django request/response was never going to cut it.
Open Source, Freelance, Full-Time: Where wdio-wait-for Came From
How chasing the same wait-condition bug across a full-time job and a freelance contract turned into an upstream contribution to a WebdriverIO community package.
Selenium Grid on a Budget: Local Distributed Testing Without Cloud Costs
When a self-hosted Selenium Grid beats BrowserStack or Sauce Labs, when it genuinely doesn't, and a real Grid 4 setup with Chrome, Firefox, and Appium nodes.
What I Learned Contributing to WebdriverIO's Official Docs
I wrote the official file-download best-practices page for webdriver.io because I kept hitting the same undocumented problem in production — here's what I learned about the problem and about contributing docs to a real OSS project.
Building a VPN Client From Scratch: A Python ShadowSocks Desktop App
Why I built ProxyPal, a macOS ShadowSocks client in Python, and what it actually takes to manage a background proxy process without losing your mind.
Cypress 16: What's New (and What Broke)
A practical upgrade guide to Cypress 16 — the breaking changes you have to fix, the flakiness fixes worth having, and an honest look at the new AI features.
Playwright's UI Mode Is Now How I Debug Everything
How Playwright UI Mode replaced console.log, --debug, and headed-mode guesswork as my default way to write and fix tests.
From Full-Stack to Framework Architect: How My Role Actually Changed
My career has moved through three genuinely different kinds of engineering, not one continuous upward line — here is what actually transferred between them and what did not.
Freelancing While Full-Time: What a Year and a Half on Upwork Taught Me
For a year and a half I worked a full-time job and freelanced on Upwork at the same time. Here is what that actually taught me, and what it cost.
Building an Anti-Detection Scraping Engine: What Actually Beats Cloudflare and reCAPTCHA
A layer-by-layer breakdown of how modern bot detection actually works — network, browser, and behavioral — and the countermeasures that actually hold up in production.
One Framework, Three Platforms: WebdriverIO for Web, Android, and iOS
How a factory pattern let one WebdriverIO codebase drive web, Android, and iOS tests instead of maintaining three separate suites.
Recursive vs. Loop-Based Traversal for Variable-Depth Automation Flows
A fixed-depth loop breaks the moment your flow has variable depth. Here is why recursion fixed it, and the real limits on when recursion is the right call.
Building a FastAPI Microservice for PDF Data Extraction
How and why I pulled PDF data extraction out of our scraping bots into its own FastAPI service, and the factory pattern that lets it handle wildly different document layouts.
Credential Management at Scale: A State Machine for Thousands of Bot Accounts
Managing thousands of bot accounts taught me that "just retry with a different password" is not a strategy — it is how you get every account you own banned in a week.
Migrating From Cypress to Playwright Without Touching a Test File
How an adapter layer let us swap Cypress for Playwright by changing one file instead of rewriting hundreds of tests — and what that approach quietly doesn't solve.
From 8 Hours to 2: Cutting a WebdriverIO Suite's Runtime by 75%
How I took an inherited 100+ file WebdriverIO suite down to 60 files and an 8-hour runtime down to 2, and where each hour of savings actually came from.
How I Unified 23 Payer Integrations Into One System in 2 Weeks
I inherited 23 separate bot integrations, each built differently, each with its own way of dying. Here is how I unified them into one system without missing the deadline.
What is Patchright? The Playwright Fork Nobody's Talking About
Patchright is a patched version of Playwright that fixes the detection leaks that get your scrapers blocked. Same API, same code — just undetectable.
CI/CD for Playwright Tests: A Real GitHub Actions Setup
A production-grade GitHub Actions workflow for Playwright — parallel sharding, caching, HTML reports as artifacts, and Slack notifications when tests fail.
Python Type Hints: A Practical Guide for Test Engineers
Type hints aren't just for application code. Used well, they make your test helpers, fixtures, and page objects dramatically easier to maintain.
Why I Switched from Selenium to Playwright (And What I Got Wrong About Selenium 4)
Three years of Selenium, then a switch to Playwright that stuck. A full comparison — including the Selenium 4 features that have genuinely closed part of the gap, and the one architectural difference that hasn't.