Professionalism, Craftsmanship, Discipline
Without wanting to get into a long war on the definitions of “function” and “method” I’d just like to say upfront that I use the terms pretty much interchangeable. In my World both are blocks of code that live in a class or struct, that you can call by name, optionally pass arguments to and optionally return a result. In a semantic war I’m sure you can pick on differences, I hope that the context here makes the meaning clear. I more often use the word “function” than “method”, not sure why except I think it’s more widely used across a range of different languages.
Functions should do just one thing.
Functions should be small, 5 or 6 lines is fine, 10 or more is a red flag for a possible SRP violation. For really long functions, maybe you have a class waiting to break out.
Functions operate at only one level of abstraction.
Arguments:
- Zero arguments is great
- One argument (monadic) is fine
- Two arguments (dyadic) OK
- Three arguments (tryadic) sometimes.
- More than three arguments is a sign something’s wrong (SRP, nascent class?)
- Avoid boolean arguments if you can
- Avoid output & ref arguments if you can
Side Effects:
Side effects are bad. basically, your code is lying about what it does and you’re setting a trap for the developers that follow you if you allow side effects.
Command Query Separation:
Functions do something or they answer a question. If your function returns something other than void then it shouldn’t something other than return a value.
Prefer exceptions to returning error codes:
If there’s an error throw an exception unless you really have a performance constraint (see Exceptions).
For find type operations, where I don’t want to throw an exception when nothing is returned, I prefer to send back an empty result collection, where more than one result is expected, or to wrap my result in a object that includes any error code alongside the result…
class FindSomethingResult
{
int ResultCode { get; init;}
Something AThing { get; init; }
}
Extract try/catch into functions in their own right…
# Not good
void DoSomething()
{
SetupSomething();
SetUpSomethingElse();
try
{
DoYourThing();
}
catch (Exception ex)
{
# handle the exception
}
}
# Better
void DoSomething()
{
SetupSomething();
SetUpSomethingElse();
DoTheThing();
}
void DoTheThing()
{
try
{
DoYourThing();
}
catch (Exception ex)
{
# handle the exception
}
}
Functions should do only one thing and error handling is one thing.
DRY
Don’t Repeat Yourself. Cut & paste is a really bad way to extend your code. where you see the same code repeated look to introduce new functions or maybe new classes, extension methods etc.
In my typical workflow I’ll allow repetitions to arise during my initial attempt to get a passing test and then refactor them out in the green phase.