Avoid abbreviations
- Often there are multiple ways to abbreviate a name, but usually only one unabbreviated name. Don't make your users guess.
- Sometimes it's tempting to abbreviate names, but consider that having consistency across libraries is often more important, than a particularly nice or clever abbreviation.
- Users can rename things on import. This makes it straightforward to look up an abbreviated name, if the user decides to abbreviate something.
Avoid symbolic names
- Sometimes it is tempting to pick a symbolic name, but consider that it makes it hard for users to search for these names on the web, and causes issues with pronouncing these names.
- As a general rule of a thumb: If the cost/benefit ratio of a symbolic name is lower than
???
, it's probably not worth it.
Constructors
- Try to have only one constructor per class.
- The constructor arguments should directly correspond to fields, and shouldn't do much more than initializing the instance and ensuring that the instance is in a valid state after construction.
Factories
-
The rules from Constructors should be treated as recommendations.
-
Avoid overloading
apply
, opt for separate methods with descriptive names instead. -
Use
fromX
if there is a considerable difference between the arguments to the method and the values of the instance, e. g. when parsing or otherwise transforming the inputs before passing them to the constructor:class Point(val x: Double, val y: Double) object Point { def apply(x: Double, y: Double) = new Point(x, y) // Good def apply(str: String) = { // Avoid! val xy = str.split(":") assert(xy.length == 2) new Point(xy(0).toDouble, xy(1).toDouble) } def fromString(str: String) = { // Better val xy = str.split(":") assert(xy.length == 2) new Point(xy(0).toDouble, xy(1).toDouble) } }
Methods
- Use
toX
/to[X]
for changing the data structure of a value, e. g.someArray.toList
,123.toDouble
. - Use
asX
/as[X]
for more fundamental conversions/transformations, like marshaling, e. g.userDatabaseRow.as[User]
,userInstance.as[XML]
.
Parameters
-
Define types to constrain the inputs. This makes it easier to reject wrong values, reduces the necessary amount of validation inside the methods and helps the user to use the method correctly:
def addEmail(email: String) // Avoid! def addEmail(email: EMail) // Better
-
Avoid long parameter lists. If they cannot be avoided, encourage users to use named parameters for clarity.
-
Avoid boolean parameters. Use enum types instead to increase clarity and allow for future extensions:
def checkAccess(isAdmin: Boolean) // Avoid! checkAccess(true); checkAccess(false); ... def checkAccess(role: Role) // Better checkAccess(Role.ADMINISTRATOR); checkAccess(Role.DEVELOPER); checkAccess(Role.INTERN); ...