How I Learned Testable Design by Breaking My Own ATM System:
** It Started With a "Working" System**
I built an ATM simulator. It worked. At least, that's what I thought.
From a developer's perspective, everything looked fine:
- Log in ✔
- Check balance ✔
- Deposit and withdraw ✔
- Transfer funds ✔
Then I asked a simple question:
How do I know this system actually works correctly?
That question changed everything.
** The Illusion of "It Works"**
At first, I was testing informally:
- Try a valid input → works
- Try another valid input → works
I felt confident. But I wasn't testing. I was confirming my own assumptions.
** The Turning Point: When I Tried to Test Properly**
I decided to approach my own system like a QA engineer.
Not casually, but systematically.
That's when I realised something uncomfortable:
I didn't actually have clearly defined expected behaviour.
** From Code to QA: Where Everything Became Real**
I created a separate repository dedicated to manual testing.
Not random testing, but a structured QA workflow:
- Requirements
- Acceptance criteria
- Test plan
- Test scenarios
- Test cases
- Execution results
- Defects
** Step 1: Writing Requirements Changed Everything**
Before writing a single test case, I had to answer:
What exactly is the system supposed to do?
So I formalised requirements like:
- Authentication rules
- Deposit validation
- Withdrawal constraints
Each requirement included clear acceptance criteria.
** First Major Realisation**
I had written code before defining behaviour. That's backwards.
** Step 2: Designing Test Cases Exposed Gaps**
When I started writing test cases, something interesting happened.
I began asking questions like:
- What happens if the user enters text instead of a number?
- What if they withdraw exactly the balance?
- What if they transfer to an invalid account?
These were not edge cases anymore. They were missing behaviours in my design.
** The Moment My System Started Breaking**
When I executed my test scenarios, I found real issues.
** Deposit Validation Issues**
- Non-numeric input was not handled clearly
- System behaviour was inconsistent
** Transfer Usability Problem**
- Error messages were too generic
- The user did not know what went wrong
** Input Handling Problem**
- The system returned to the menu instead of re-prompting
- The user flow was broken
These were not coding mistakes. They were design problems.
** Test Execution Made It Real**
** Execution Summary**
From actual execution:
- ✅ Authentication → Passed
- ✅ Balance → Passed
- ❌ Deposit → Failed
- ❌ Transfer → Failed
- ❌ Input Handling → Failed
All backed by:
- Evidence screenshots
- Execution notes
- Scenario tracking
** Second Major Realisation**
Without execution tracking, testing is just opinion.
** Step 3: Defects Told Me the Truth**
I documented each defect clearly:
- Steps to reproduce
- Expected vs actual behaviour
- Evidence
- Impact
I did not use tools like Jira. I did not need them. Because the value was not in the tool, it was in the clarity of thinking.
** The Most Important Lesson: Design Impacts Testing**
At this point, something became very clear.
** Developer vs QA Mindset**
| Developer Thinking | QA Thinking |
|---|---|
| It works | What can break? |
| Focus on happy path | Focus on edge cases |
| Code-focused | Behaviour-focused |
** Critical Insight**
Poor design creates hidden defects. Testing simply reveals them.
** SDLC Reflection: Where This Matters in Real Life**
This experience made me think beyond my project.
** What Happens in Real Projects?**
If you skip proper planning:
- Requirements are unclear
- Test cases are inconsistent
- Bugs are discovered late
- Fixes become expensive
** Cost of Poor Planning**
- Developers rework logic
- Testers rewrite cases
- Deadlines slip
- Confidence drops
** Looking Back at My ATM Design**
Now I see my system differently.
Before Testing
This works fine
After Testing
- Validation logic incomplete
- Error messaging unclear
- User flow inconsistent
** The Shift That Changed Everything**
I stopped asking: Does it work?
And started asking: Can it fail safely?
** What This Means Going Forward**
Because I designed the system with separation of concerns:
- I can now add an API layer
- I can automate tests using Pytest
- I can introduce UI automation
This is no longer just a project. It is a foundation for real QA and automation work.
** Final Takeaways**
- Writing code is not enough
- Testability is a design decision
- Requirements drive testing
- Testing reveals design flaws
- Real QA is structured, not random
** Closing Thought**
I started this project to build an ATM system, but I ended up learning something much bigger. The moment I stopped treating QA as something you do after coding and started treating it as a design constraint from the beginning, my code improved before I even wrote it. I didn't just learn how to test software - I learned that the most valuable test cases are the ones that make you realise your design was incomplete
** Repositories**
ATM Simulator
https://github.com/enayetrashid/atm-simulator
Manual Testing Practice
https://github.com/enayetrashid/manual-testing-practice



Top comments (0)