The God Function:
When One Method Does Everything

The function was 347 lines long. Fourteen engineers had modified it. None of them had rewritten it. The PR that finally broke it was six lines long.

PR · 6 lines changed

The function was called processOrder. It was 347 lines long. It validated the cart, checked inventory, applied discounts, calculated tax, charged the card, created the order, decremented stock, sent an email, enqueued a webhook, and updated analytics.

A junior engineer added a referral credit at line 289. If the stock decrement failed and the order rolled back, the customer kept the credit anyway. Free money, forever. Three reviewers approved it. None read past line 100.

Every codebase has a god function. You know the one. It's the function nobody wants to touch, everybody has to touch, and nobody can fully understand. It started as 40 lines — a clean, reasonable implementation of a straightforward feature. Then it grew. Someone added error handling. Someone added a notification. Someone added a special case for enterprise accounts. Someone added a flag for A/B testing.

Each addition was small. Each addition was reasonable. Each addition made the function a little harder to review. And at some point, it crossed a threshold — not a technical one, but a human one — where no reviewer could hold the entire function in their head at once.

That's when it became dangerous. Not because it stopped working, but because it became unreviewable. And unreviewable code is where bugs go to hide.

Why god functions survive

The obvious question is: why doesn't someone just refactor it?

The answer is the same everywhere. The function works. It's tested (sort of — the test file is 800 lines of mocks). It touches payments, and nobody wants to be the person who broke payments trying to make the code "cleaner." The refactor would take a week, it would touch a dozen files, the diff would be massive, and the reviewer would face exactly the same comprehension problem they already have — except now the code is unfamiliar and reorganized.

So nobody refactors it. Instead, they add six lines at line 289 and hope for the best.

This is the lifecycle of every god function:

01
Birth
Clean & single-purpose
40 lines. One responsibility. Easy to read, easy to test, easy to review.
02
Growth
Each PR adds a concern
Error handling. Notifications. Enterprise flags. A/B tests. Each one reasonable in isolation.
03
Critical mass
Too big to refactor safely
The refactor is a week of work. It touches payments. Nobody volunteers.
04
Decay
Bugs hide in the interactions
Responsibilities that shouldn't share a scope start colliding. Ordering matters but isn't enforced.

The tragedy is that every individual PR in this lifecycle is reasonable. No single PR made the function unreviewable. It was the accumulation — death by a thousand reasonable pull requests.

What a god function looks like in a diff

Here's the thing about god functions: you almost never see the whole function in a PR. You see a change to the function. The diff shows you the six lines that were added, with maybe twenty lines of surrounding context. The other 320 lines are folded away.

This is why god functions are a code review problem specifically. The author knows where their change goes because they've been reading the function for an hour. The reviewer sees a narrow window — a slot in a wall — and has to determine whether the change is correct based on what's visible through that slot.

Here's what the diff for the referral credit PR actually looked like:

Python checkout/service.py — processOrder()
      await send_confirmation_email(order)

      # Apply referral credit
      if order.referral_code:
          await apply_credit(order.user_id, Decimal("5.00"))

      await enqueue_webhook("order.completed", order)
      await decrement_stock(order.items)

Six lines. Clean. Readable. The if check is correct. The function call looks right. The amount is reasonable. If you're reading this diff in isolation — which is how most reviewers read it — there is nothing wrong.

But the diff doesn't tell you that send_confirmation_email happens before the stock decrement. It doesn't tell you that if decrement_stock fails, the order gets rolled back — but the email has already been sent and the credit has already been applied. Free money, forever, for anyone with a referral code and an out-of-stock product.

You can't see any of that through the slot. You'd have to open the full file, read all 347 lines, understand the order of operations, understand the error handling (or lack thereof), and then evaluate whether inserting a credit at line 289 is safe given everything that happens before and after it.

No reviewer does this. Not because they're lazy, but because it takes twenty minutes, and they have nine other PRs to review before standup.

The five bugs that god functions breed

God functions don't just make code harder to read. They create specific categories of bugs that don't exist in well-structured code.

1. Ordering bugs

When a function does ten things in sequence, the order matters. But the order isn't documented, isn't enforced, and often isn't obvious. A new step inserted in the wrong position creates a dependency violation.

The referral credit bug is an ordering bug. The credit must be applied after inventory is confirmed, not before. But nothing in the code enforces that constraint. The function is just a list of steps, and any step can be inserted anywhere.

2. Partial failure bugs

A 347-line function that does ten things will inevitably have some operations that succeed and some that fail. If those operations aren't wrapped in a transaction or a saga pattern, a failure at step 7 leaves steps 1–6 in a committed state that step 7 was supposed to validate.

The classic version: charge the card (step 4), then create the order record (step 5). If step 5 fails — database timeout, unique constraint violation, anything — the card has been charged but no order exists. The customer paid for nothing.

3. Shared mutable state bugs

A long function accumulates local variables. By line 300, there are fifteen variables in scope, some of which were set at line 40 and haven't been touched since. A new piece of code accidentally shadows one of them, or mutates an object that a later step depends on.

Python checkout/service.py — variable shadowing
# Line 45
discount = calculate_discount(cart)

# ... 240 lines later ...

# Line 285 — new code in the PR
discount = referral_discount(order.referral_code)  # shadows the original

# Line 310 — uses the original discount for tax calculation
tax = calculate_tax(order.subtotal - discount)  # now using wrong discount

The reviewer sees the new discount assignment on line 285. They don't see that a different discount was set on line 45 and is still used on line 310. The variable name is reused. The bug is invisible in the diff.

4. Test impossibility

God functions are either untested or tested with massive integration tests that mock half the universe. Unit testing a 347-line function that calls the database, the payment provider, the email service, and the webhook queue requires mocking all four — and the test ends up being a mirror of the implementation, asserting the exact sequence of calls in the exact order.

The result: the function is either not tested (bugs ship freely) or tested so tightly that nobody dares change it (bugs also ship freely, because the test suite prevents refactoring that would prevent bugs).

5. The "it's faster to add than to fix" trap

This is the meta-bug — the one that creates all the others. When a function is already 300 lines long, adding 6 more lines feels harmless. The alternative — refactoring into smaller, composable pieces — feels risky and time-consuming. So every engineer makes the rational local decision: add the lines, ship the feature.

Each decision is rational. The cumulative result is a function that nobody can review, nobody can test, and nobody can refactor. The god function grows because it's always easier to feed it than to kill it.

How to review a PR that touches a god function

You can't fix the god function in a code review. That's a refactoring effort, and it should be its own project. But you can prevent the PR in front of you from making things worse.

  • Read the full function, not just the diff. Yes, it takes fifteen minutes. But if the function is 300+ lines and the PR inserts code in the middle, you cannot evaluate the change without understanding the context.
  • Ask: where in the sequence does this go, and why there? If the author added code between the email and the webhook, ask why it's not after the stock decrement. If they can't articulate why the position matters, the position is probably wrong.
  • Ask: what happens if this new code fails? If the new step throws, which previous steps have already committed? Is the overall operation still in a consistent state?
  • Ask: does this PR make the function longer? Not as a style nit — as a substantive concern. "Can we extract the new behavior into its own function and call it from here? That way it can be tested independently."

That last comment is the most important one you can leave. Not because it fixes the god function — it doesn't. But it stops the bleeding. It prevents the function from growing by another 6 lines. And if three reviewers say the same thing on three consecutive PRs, eventually someone will schedule the refactor.

How to say it without being a jerk

The author didn't create the god function. They inherited it. They need to ship a feature, and the god function is where that feature needs to go. Telling them "this function is too long" is accurate and unhelpful. They know it's too long. They don't have time to fix it.

Python checkout/service.py — processOrder()
      await send_confirmation_email(order)

      # Apply referral credit
      if order.referral_code:
          await apply_credit(order.user_id, Decimal("5.00"))

      await enqueue_webhook("order.completed", order)
      await decrement_stock(order.items)
SR
Senior Reviewer · just now
The change itself looks correct, but I'm concerned about the position — the referral credit is applied before inventory is confirmed, which means a stock failure would roll back the order but not the credit.

Short-term: could we move the credit application to after decrement_stock, or make it conditional on the stock check succeeding?

Longer-term: this function is carrying a lot of responsibilities and it's getting hard to reason about ordering and failure modes. Worth flagging for a future refactor — happy to pair on breaking it up.

That comment does four things: names the specific bug, suggests an immediate fix, flags the structural problem without blaming the author, and offers to help. It's direct, useful, and kind.

The god function is a team problem

No individual engineer creates a god function. The team creates it, one PR at a time, over months or years. The engineer who adds line 347 is no more responsible than the engineer who added line 40. The function grew because the team's review culture allowed it to grow — because fourteen reviewers saw the function getting longer and none of them said "we need to stop adding to this."

The god function is a signal. It tells you that the team has been prioritizing short-term velocity over long-term maintainability. It tells you that reviewers have been reading diffs instead of code. It tells you that nobody has been asking "what happens when this function is 400 lines?" because the answer is uncomfortable and the refactor is expensive.

The god function is also an opportunity. It's the single highest-leverage refactoring target in your codebase — the one function where splitting it into five smaller functions would reduce bugs, speed up reviews, enable testing, and make every future feature easier to build.

Kill the god function. Or at least, stop feeding it.

Sharpen your architecture instinct

Architecture PRs — separation of concerns, god classes, coupling, dependency injection — are some of the most nuanced reviews in Code Review Academy. They're not about spotting a single bug. They're about reading a system and asking whether it's structured to survive the next twelve months of changes.