This is a collection of anti-patterns that I have observed happening out in the wild.
Default parameters and too many binary parameters
Let’s take a look at the following snippet.
override def interval(lowerBound: Numeric, upperBound: Numeric, maxObserved: Numeric, minInclusiveInMiddle: Boolean = false, useUpperBound: Option[Numeric] = None): Interval[Numeric] = Interval( Bound(lowerBound, inclusive = if (minInclusiveInMiddle) true else false), if (upperBound == maxObserved) useUpperBound.map(ub => Bound(ub, inclusive = true)) .getOrElse(Bound(maxOrderedValue, inclusive = false)) else Bound(upperBound, inclusive = true) )
Can you describe in a single sentence what this method does? I can’t. Why? Because there are at least four different methods hidden here. Default parameters should be used only if the behavior of the function does not significantly change based on those parameters.
Special values without new types
This is highly problematic because it makes it difficult to track which values require special handling, and which do not. You end up with code like this:
And all hell breaks loose when it turns out that
outOfRangeValue is one of possible values for
foo as well.
It is much more preferable to either define a new type,
or a newtype with custom constructors and deconstructors.