- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Big O Notation for Working Developers: Finding the Loop That Doesn't Scale
About Post
Here's a story that plays out on almost every team sooner or later. The code passed review. The tests were green. It handled the sample file of 200 rows instantly. Then someone uploaded a real import file, and the request timed out.
Nothing was "wrong" with the code. It was just O(n²), and 200 rows was small enough to hide it.
Big O has a reputation as interview trivia, something you memorise for a whiteboard and forget. I think that's backwards. It's one of the most practical ideas in programming, and you don't need any maths beyond "what happens when the data doubles?"
The one question Big O answers
Big O describes how the work grows as the input grows. Not how fast code is in seconds, but how it scales.
So the useful question is: if my data doubles, what happens to the work?
| Notation | Data doubles, work... | Everyday example |
|---|---|---|
| O(1) | stays the same | Reading $array['key'], isset(), a cache hit |
| O(log n) | grows by one small step | Looking up a row through a database index |
| O(n) | doubles | A single foreach, in_array(), a full table scan |
| O(n log n) | a little more than doubles | Sorting: sort(), usort(), ORDER BY without an index |
| O(n²) | quadruples | A loop inside a loop over the same data |
That last row is where most real trouble lives. At 200 items, n² is 40,000 steps: nothing. At 20,000 items it's 400 million. Same code, a hundred times more data, ten thousand times more work.
The hidden loop
Here's the thing: the nested loops that hurt are rarely two obvious foreach blocks. One of them is usually hiding inside a function call.
// Find invoices whose tenant is no longer active
$orphaned = [];
foreach ($invoices as $invoice) {
if (! in_array($invoice->tenant_id, $activeTenantIds)) {
$orphaned[] = $invoice;
}
}
One loop, right? But in_array() walks through $activeTenantIds from the start every time. That's a loop inside a loop: O(n × m). Fine with a few hundred of each, painful with tens of thousands.
The fix is to turn the list into a lookup table once. PHP arrays are hash tables, so isset() on a key is O(1) on average:
$active = array_flip($activeTenantIds); // values become keys, once: O(m)
$orphaned = [];
foreach ($invoices as $invoice) {
if (! isset($active[$invoice->tenant_id])) {
$orphaned[] = $invoice;
}
}
Now the whole thing is O(n + m). Same result, and it scales in a straight line.
The same pattern appears everywhere under different names:
- Laravel collections:
->contains(),->firstWhere()or->where()inside a loop. Use->keyBy('id')once and look up by key. - JavaScript:
array.includes()orarray.find()inside.filter()or.map(). Build aSetorMapfirst. - Queues of work:
array_shift()in a loop. It re-indexes the whole array on every call, so draining an array that way is quietly O(n²).
Big O in the database
Databases are where Big O stops being theoretical, because tables grow forever and nobody resets them.
- No index:
WHERE email = ?is a full table scan. O(n). Fine at a thousand rows, slow at ten million. - With an index: the database walks a B-tree, which is O(log n). Going from a thousand rows to a million adds only a few extra steps. That's why indexes feel like magic.
- A join on an unindexed column can become a nested loop across two tables: hello again, O(n × m).
- N+1 queries are O(n) in round trips. Each query is fast, but a hundred rows means a hundred and one trips to the database. Eager loading with
with()makes it a constant two.
When I look at a slow page, this is the first lens I use: which part grows with the data, and how fast?
When Big O doesn't matter
Big O describes growth, so it ignores constants. That has two practical consequences:
- For small, bounded data, it barely matters. A nested loop over the twelve months of a year is fine forever. Don't make code harder to read to optimise something that can't grow.
- Constants can beat complexity. One database query that's O(n) in theory will usually beat a thousand O(1) cache calls over the network. Network round trips, disk access and memory dominate real performance at everyday sizes.
The real question isn't "is this O(n²)?" It's "is n bounded?" If n is the number of months, statuses or settings, relax. If n is users, invoices, rows, uploads or log lines, it will grow, and it will find your quadratic loop eventually.
The practical rule: whenever you see a lookup inside a loop (in_array, contains, find, a query), ask what happens when both lists are a hundred times bigger. If the answer is "a lot", build a lookup table or let the database do it with an index.
The cheat sheet
- Ask "what happens when the data doubles?"
- Look for hidden loops inside function calls.
- Turn repeated searches into key lookups:
array_flip,keyBy,Set,Map. - Indexes turn O(n) scans into O(log n) lookups.
- Test with realistic data volumes, not just the seed data.
- Don't optimise what can't grow.
What's the sneakiest O(n²) you've found hiding in a codebase? Mine are almost always an innocent-looking in_array inside a loop.

Be first to comment it...