Live data from Hacker News

Ask HN: How do you deal with 2000 line functions?

news.ycombinator.com

31–40 of 72 posts

Re: Ask HN: How do you deal with 2000 line functions?

#31
post #8

Earlier quoted context omitted.

Yep, this is the only way to handle these beasts. Don't touch anything. Make a ton of tests. Spend some time validating those tests. Get someone to write off that those tests are sufficient. Start refactoring!

For a function with 2,000 lines of code, we have to be honest with ourselves and accept that testing will never be sufficient; if there's 2,000 lines of code, you can bet good money that there's global state manipulation as well. Making the assumption that anybody is capable of sufficiently testing such functions and subsequently re-writing them will only introduce new bugs and old regressions. Seems harsh, but I've…

I've been here too. If possible, try and pull someone in who's familiar with the code to hopefully help you test and simplify it.

2000 lines of code is a massive amount of behaviour to understand. There's a minuscule chance you can infer all that behaviour from reading the code unfortunately.

Re: Ask HN: How do you deal with 2000 line functions?

#33
post #29

Something I found to be really useful is to remove all statements inside the code, so you'll be left with large skeleton structures. Look at it from a distance, (small font), see some patterns. Can you split it up? getting a feel for the structure of the function helps me understand it. Ex: function someFunction () { if () { for () { if () { } else { } } if () { for () { } } } }

Ah I do this with if() block folding in Vim.

In this case, the developer actually has return statements inside some if blocks. A general point that has stood out from all comments is to first understand how the function achieves what the function is trying to achieve.

Re: Ask HN: How do you deal with 2000 line functions?

#34
I remember linear lists of 1000+ if-statements. These were "state machines", popular form of real-time multitasking 50 years ago. Nothing wrong with that. You might make an analyzer which recognizes longer series of events and impossible states, but why bother. This is a programming paradigm, best you can do is to go with the flow.

Re: Ask HN: How do you deal with 2000 line functions?

#35
post #8

Earlier quoted context omitted.

Yep, this is the only way to handle these beasts. Don't touch anything. Make a ton of tests. Spend some time validating those tests. Get someone to write off that those tests are sufficient. Start refactoring!

For a function with 2,000 lines of code, we have to be honest with ourselves and accept that testing will never be sufficient; if there's 2,000 lines of code, you can bet good money that there's global state manipulation as well. Making the assumption that anybody is capable of sufficiently testing such functions and subsequently re-writing them will only introduce new bugs and old regressions. Seems harsh, but I've…

Global state is one of the trickiest problems we are trying to solve at my workplace. Our entire, 10 year old, PHP codebase relies on a single static class (and now some supporting static classes) aptly called "Meta". What does it do? Meta things.

It's basically an abstraction over the entire database layer, that has probably over 30 toggles that denote where and how it should fetch data, given a table name and a function call.

And every database call in the entire project is dependent on it.

Re: Ask HN: How do you deal with 2000 line functions?

#36
I've had to do this once. They don't teach you managing code like this! A friend gave me a copy of Working Effectively With Legacy Code[0] which helped me.

The gist of it: a strong suite of integration and unit tests. Isolate small code paths into logical units and test for equivalency.

[0] http://www.amazon.com/Working-Effectively-Legacy-Michael-Fea...

Re: Ask HN: How do you deal with 2000 line functions?

#38
post #8

Earlier quoted context omitted.

Yep, this is the only way to handle these beasts. Don't touch anything. Make a ton of tests. Spend some time validating those tests. Get someone to write off that those tests are sufficient. Start refactoring!

For a function with 2,000 lines of code, we have to be honest with ourselves and accept that testing will never be sufficient; if there's 2,000 lines of code, you can bet good money that there's global state manipulation as well. Making the assumption that anybody is capable of sufficiently testing such functions and subsequently re-writing them will only introduce new bugs and old regressions. Seems harsh, but I've…

That's why I mentioned getting a write off on the changes. If several peers, project managers, etc write off that they expect the behavior to be one of these things, covered by these other tests, and it's not, well... Everyone just got educated if and when it breaks!

Re: Ask HN: How do you deal with 2000 line functions?

#40
post #29

Something I found to be really useful is to remove all statements inside the code, so you'll be left with large skeleton structures. Look at it from a distance, (small font), see some patterns. Can you split it up? getting a feel for the structure of the function helps me understand it. Ex: function someFunction () { if () { for () { if () { } else { } } if () { for () { } } } }

To expand on that, something I sometimes find useful with giant functions with too many 'ifs' is to pull out the code that runs for each of the possibilities. Frequently I'll discover that there are actually several different overlapping functions (possibly with some common parts) mashed together with the differences wrapped in 'if'.

Ferinstance:

  def giant_method
    if thingy1?
      do_stuff
    else
      do_something_else
    end
    do_common_stuff
    if thingy1?
      finish_up_for_thingy1
    else
      finish_up
    end
  end
It's easy to spot that there are really two paths through the method (depending on what thingy1? returns) in this simplified form, but rewriting it will make it clearer even in a 2k line function:

  def giant_method
    if thingy1?
      do_stuff
      do_common_stuff
      finish_up_for_thingy1
    else
      do_something_else
      do_common_stuff
      finish_up
    end
  end
This is also useful even when the 'if' clauses aren't identical - even then, there are frequently implied relationships (for instance, if one checks if something is positive and the other checks for zero) that can simplify things.
Post reply on HN