Akintola Steve Profile picture
Apr 11 16 tweets 3 min read Read on X
A Nigerian fintech startup raised $3.2M, spent 14 months building a “bulletproof” payment platform, and launched with 40k users in week one.

Investors were celebrating.

Then in one weekend… everything collapsed.

Here’s the security blind spot that destroyed them, and the system design lessons every backend engineer in 2026 must understand.
Week one looked perfect.

Transactions flowing, dashboards stable, users happy.

Then support tickets exploded:

“Someone drained my wallet.”
“My balance shows -₦2.8M.”
“Unauthorized transfers at 3 am”
They thought it was a bug.
It wasn’t.
They traced it.

It wasn’t random.

It was structured exploitation.

SQL injection on a weak endpoint exposed user balances.

Replay attacks duplicated old transfer requests.

Session hijacking bypassed authentication entirely.

The system wasn’t “hacked once”.

It was systematically broken.
The real mistake?

Security was treated as a feature to add later.

Classic “we’ll secure it after MVP” thinking.

They had microservices, caching, load balancing…

But zero secure-by-design thinking from day one.

That decision broke everything.
What they missed is simple but critical:

Security is not a layer.

It is a system design requirement, like scalability or availability.

If your architecture starts without threat modeling, you are already exposed.
First principle: STRIDE threat modeling.

Every component must be analyzed:

Spoofing
Tampering
Repudiation
Information disclosure
Denial of service
Elevation of privilege
Because in reality, every system has an attacker model, whether you define it or not.
Example they ignored:

A login endpoint like:

SELECT * FROM users WHERE email = '$email' AND password = '$password'

One input change:

' OR 1=1 --

And suddenly authentication is bypassed.

No password needed.

That’s SQL injection.

Still happening in 2026.
Another failure:

A transfer API that accepted:

{ amount, recipient, timestamp }

Attackers intercepted a valid request and replayed it multiple times.

Same payload. Different outcomes.

Result: one ₦10,000 transfer became ₦100,000 in seconds.

No nonce. No signature validation. No protection.
Then authentication cracks:

Session tokens never expired properly.

Attackers stole JWTs from compromised devices and reused them across IPs.

No device binding. No token rotation.

So even after password changes, access remained active.

That is not a bug.

That is broken auth design.
Fixing this requires more than patches.

You move to Zero-Trust Architecture:

mTLS between services
Short-lived tokens
Request signing (HMAC or asymmetric)
Replay protection using nonces + timestamps
Device-bound sessions

Every request must prove it belongs.
Minimum baseline if you’re not there yet:

Input validation everywhere
WAF with OWASP Core Rules
Prepared statements only
Strict CORS + CSP policies
Rate limiting at gateway
Dependency scanning (Snyk, Dependabot)
Security chaos testing

No shortcuts.
The startup rebuilt everything.

STRIDE modeling first.
Replay protection added.
SQL injection eliminated via strict ORM + prepared queries.
JWT system redesigned with rotation + expiration enforcement.
Anomaly detection introduced for transaction spikes.

Attacks dropped by 94%.

Same product. Different assumptions.
Most systems are designed for ideal conditions.

Fast networks. Clean inputs. Trusted users.

But real systems face:

injected SQL payloads
stolen sessions
replayed requests
compromised clients

If you don’t design for attackers, you are designing for failure.
Bookmark this.

If you’re building anything serious in 2026, be it fintech, healthtech, e-commerce, or govtech.

Security is not optional.

It is system design.

Not a patch. Not a feature.

Architecture itself.
If this made you rethink your backend design, share it.

Tag an engineer who might need to learn from this too.
NB:
Fictional but real technical lessons shared.

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Akintola Steve

Akintola Steve Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @Akintola_steve

May 24
There are different types of message brokers.
And picking the wrong one can overcomplicate your entire architecture.

Not every system needs Kafka.
Not every workload fits RabbitMQ.
Not every async job should touch Redis Streams.

I’ll explain, let’s read
RabbitMQ →
Best for reliable task queues.

Need:
• retries
• dead letter queues
• job acknowledgements
• ordered workers

Perfect for:
payments, emails, background jobs, OTP processing.
Kafka →
Built for massive event streaming.

Best when:
• Millions of events are flowing
• Multiple services consume same events
• Replay ability matters
• Analytics pipelines exist

Perfect for:
Uber events, clickstreams, fraud systems.
Read 5 tweets
May 23
Backend Engineers throw Kafka into every architecture nowadays like it’s a compulsory tech stack.

Meanwhile, many applications genuinely do not need Kafka at all.

So, when exactly should Kafka actually be used?
Asynchronous background processing at scale.

A user uploads a video, submits a huge report, or places an order.

You don’t want them waiting 10–30 seconds for processing.

Push the job into Kafka.
Consumers process it in the background while the API returns instantly.

This is how large platforms handle:
• Video encoding
• PDF generation
• Image resizing
• Email campaigns
Event-driven architecture & service decoupling.

Multiple services need to react to the same event, like:
“UserPlacedOrder”
Instead of chaining HTTP requests between services and creating cascading failures…

One service publishes an event to Kafka.
Other services consume it independently.

This is the foundation of properly decoupled microservices.
Read 12 tweets
May 20
“Who deleted the transactions?”

The engineering call went completely silent.

Because ₦187 million worth of transaction records had disappeared from production.

And nobody could explain how.
At first, everybody assumed it was probably:

A frontend bug.
Cache issue.
Delayed replication.
Bad query.
Normal production drama.

Then, finance joined the call.
“This is not a UI issue.”

The rows were actually gone from the database.

Deleted.
Read 24 tweets
May 20
While building systems, engineers should choose databases with the mindset that not every data model will age gracefully with their application.

Most teams pick MongoDB because “it’s flexible.”
Others default to Postgres because “it’s reliable.”
Then the data layer becomes the bottleneck.

Choosing the right DB is designing your foundation so it doesn’t collapse under real usage.
MongoDB wins when:

• Your data is naturally document-shaped
(User profiles, content, catalogs, configs)

• Schemas change frequently during rapid iteration

• You need true schema flexibility without painful migrations

• Horizontal scaling and sharding feel natural for your workload
MongoDB also shines when:

• Access patterns are mostly single-document reads/writes

• You’re in the early stages, and the full shape of data isn’t known yet

• You want developer speed and simpler queries for common operations

It lets you move fast when requirements are still evolving.
Read 10 tweets
May 18
One thing I’ve noticed is that a lot of people building Fintech applications actually do not understand how payment systems work.

So I made a list of 10 payment terms that most backend engineers trying to break into the Fintech space must know.

Would be helpful during interviews, too.
Authorization

The bank approves the transaction and reserves the money.

Example:

You order an Uber.
Your card gets checked first before the trip ends.
Money is reserved.
Not fully collected yet.
Capture

This is when the merchant finally collects the authorized money.

Example:

A hotel blocks ₦200k during check-in.
You get fully debited after checkout.
Read 10 tweets
May 15
Pelumi’s e-commerce backend was handling 200 users comfortably.

Then they ran a promo.

800 users hit the platform in 45 minutes.

The server didn’t crash because of traffic.

It crashed because of one line of code that had been sitting in the codebase since day one
Pelumi had built a product listing page for her fashion marketplace.

NestJS backend.
Prisma ORM.
PostgreSQL database.
Clean architecture.
Well-structured code.
Everything looked perfect locally.
The endpoint fetched all products and, for each product, returned the seller’s name and rating.

The code looked innocent:

const products = await prisma.product.findMany()

for (const product of products) {
product.seller = await prisma.user.findUnique({
where: { id: product.sellerId }
})
}

Worked perfectly in development.
Read 15 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(