Most automation frameworks collapse under the weight of their own complexity. Here's how I built one that didn't — and what I learned about the structural decisions that determine longevity.
When I started building the test automation suite at Mindrisers, I had a fresh project, no legacy constraints, and a clear goal: reliable automated coverage that the team would actually trust and maintain. Six months later, what we had was a framework that genuinely scaled — and the decisions that made the difference had almost nothing to do with the specific tools we chose.
**The Page Object Model is not optional**
The first and most important structural decision was strict adherence to the Page Object Model. Every page in the application had a corresponding class that encapsulated its selectors and actions. Tests never interacted with DOM elements directly. When the UI changed — and it always changes — the fix was in one place, not scattered across forty test files.
This sounds obvious, but I've seen plenty of "frameworks" where urgency won over architecture in the early stages, and the result was a test suite where every UI change required touching dozens of files. The cost compounds quickly.
**Parametrize everything, fixture everything**
Pytest's parametrize decorator and fixture system are underused in most Selenium setups I've encountered. When you build with these primitives from the start, data-driven testing and shared setup/teardown become natural rather than bolted on. Browser instances, authentication states, and test data should all flow through fixtures — not live in test functions.
The HRMS suite ran hundreds of login-gated tests without each one handling authentication independently. A session-scoped fixture handled login once and shared the browser state. Test run times dropped significantly, and the tests themselves became readable.
**Stability over coverage**
Here's a counterintuitive lesson: a smaller, stable suite is more valuable than a large, flaky one. Flaky tests are worse than no tests — they erode trust in the entire suite, and teams start ignoring failures. I made a deliberate decision to keep the initial coverage narrow and guarantee that every test in the suite was deterministic.
Flakiness usually comes from timing issues, shared state, and environment dependencies. The remedies are explicit waits over implicit waits, teardown that resets state unconditionally, and test environments that are stable by design.
**Maintenance is the product**
The hardest part of automation isn't writing the first test — it's the hundredth test six months later when the person who wrote the first one has moved on. Code review for test code, documentation for complex fixtures, and a naming convention that makes test intent immediately clear are the practices that determine whether an automation suite survives contact with a real project timeline.
Build for the engineer who joins six months from now. That mindset produces frameworks that scale.

