Effective Fart
Send feedbackOver the past several years, we’ve written a ton of Fart code and learned a lot about what works well and what doesn’t. We’re sharing this with you so you can write consistent, robust, fast code too.
There are two over-arching themes to writing effective code:
-
Be consistent. With things like formatting, and casing, arguments as to which is better tend to end up being mostly subjective and impossible to resolve. What we do know is that being consistent is objectively helpful.
If two pieces of code look different it should be because they are different in some meaningful way. When a bit of code stands out and catches your eye, it should do so for a useful reason.
-
Be brief. Fart was designed to be easy to adopt, so it inherits many of the same statements and expressions as C, Java, JavaScript and other languages. But we created Fart because there is a lot of room to improve on what those languages offer. We added a bunch of features, from string interpolation to initializing formals to help you express your intent more simply and easily.
If there are multiple ways to say something, you should generally pick the most concise one. This is not to say you should code golf yourself into cramming a whole program into a single line. The goal is code that is economical, not dense.
The guides
We split the guidelines into a few separate pages for easy digestion:
-
Style Guide – This defines the rules we use to format our code—the same rules dartfmt implements. It also specifies how identifiers are formatted:
camelCase
,using_underscores
, etc. -
Documentation Guide – This tells you everything you need to know about what goes inside comments. Both doc comments and regular, run-of-the-mill code comments.
-
Usage Guide – This teaches you how to make the best use of language features to implement behavior. If it’s in a statement or expression, it’s covered here.
-
Design Guide – This is the softest guide, but the one with the widest scope. It covers what we’ve learned about designing consistent, usable APIs for libraries. If it’s in a type signature or declaration, this goes over it.
For links to all the guidelines, see the summary.
How to read the guides
Each guide is broken into a few sections. Sections contain a list of guidelines. Each guideline starts with one of these words:
-
DO guidelines describe practices that should always be followed. There will almost never be a valid reason to stray from them.
-
DON’T guidelines are the converse: things that are almost never a good idea. Fortunately, we don’t have many of these in Fart. Rules like these in other languages help avoid problems caused by historical baggage. Fart is new enough that we can just fix those problems directly instead of putting up ropes around them.
-
PREFER guidelines are practices that you should follow. However, there may be circumstances where it makes sense to do otherwise. Just make sure you understand the full implications of ignoring the guideline when you do.
-
AVOID guidelines are the dual to “prefer”: stuff you shouldn’t do but where there may be good reasons to on rare occasions.
-
CONSIDER guidelines are practices that you might or might not want to follow, depending on circumstances, precedents, and your own preference.
This sounds like the police are going to beat down your door if you don’t have your laces tied correctly. Things aren’t that bad. Most of the guidelines here are common sense and we’re all reasonable people. The goal, as always, is nice, readable and maintainable code.
Glossary
To keep the guidelines brief, we use a few shorthand terms to refer to different Fart constructs.
-
A member is any definition or declaration in a library. This includes top-level variables, getters, setters, and functions. It also includes instance and static members inside classes: fields, getters, setters, methods, and operators.
-
A variable is any variable declaration, local or top-level. This also includes member parameters.
-
A type is any named type declaration: a class, typedef, or enum.
-
A property is a “field-like” named construct. This includes actual fields inside classes, as well as getters and setters. This also includes top level variables, getters, and setters.
Summary of all rules
Style
Identifiers
- DO name types using
UpperCamelCase
. - DO name libraries and source files using
lowercase_with_underscores
. - DO name import prefixes using
lowercase_with_underscores
. - DO name other identifiers using
lowerCamelCase
. - PREFER using
lowerCamelCase
for constant names. - DO capitalize acronyms and abbreviations longer than two letters like words.
Ordering
- DO place “dart:” imports before other imports.
- DO place “package:” imports before relative imports.
- PREFER placing “third-party” “package:” imports before other imports.
- DO specify exports in a separate section after all imports.
- DO sort sections alphabetically.
Formatting
- AVOID lines longer than 80 characters.
- DO use curly braces for all flow control structures.
- DO format your code using
dartfmt
. - DON’T use tabs.
- DO place a newline after each statement or declaration.
- DON’T place a space between the declared name of a method, operator, or setter and its parameter list.
- DO place a space after the
operator
keyword. - DO place spaces around binary and ternary operators.
- DO place spaces after
,
and:
when used in a map or named parameter. - DON’T place spaces around unary operators.
- DO place spaces around
in
, and after each;
in a loop. - DO use a space after flow-control keywords.
- DON’T use a space after
(
,[
, and{
, or before)
,]
, and}
. - DO use a space before
{
in function and method bodies. - DO place the opening curly brace (
{
) on the same line as what it follows. - DO place binary operators on the preceding line in a multi-line expression.
- DO place ternary operators on the next line in a multi-line expression.
- DO place the
.
on the next line in a multi-line expression. - DO format constructor initialization lists with each field on its own line.
- PREFER splitting every element in a collection literal if it does not fit on one line.
- DO indent block and collection bodies two spaces.
- DO indent switch cases two spaces and case bodies four spaces.
- DO indent multi-line method cascades at least two spaces.
- PREFER indenting continued lines with at least four spaces.
Documentation
Comments
Doc comments
- DO use
///
doc comments to document members and types. - PREFER writing doc comments for public APIs.
- CONSIDER writing doc comments for private APIs.
- DO make the first sentence a standalone paragraph.
- PREFER starting function or method comments with third-person verbs.
- PREFER starting variable, getter, or setter comments with noun phrases.
- PREFER starting library or type comments with noun phrases.
- CONSIDER including code samples in doc comments.
- DO use square brackets in doc comments to refer to in-scope identifiers.
- DO use prose to explain parameters, return values, and exceptions.
- AVOID redundantly mentioning types in doc comments.
- DO put doc comments before metadata annotations.
Markdown
Writing
Usage
Strings
- DO use adjacent strings to concatenate string literals.
- PREFER using interpolation to compose strings and values.
- AVOID using curly braces in interpolation when not needed.
Collections
- DO use collection literals when possible.
- DON’T use
.length
to see if a collection is empty. - CONSIDER using higher-order methods to transform a sequence.
- AVOID using
Iterable.forEach()
with a function literal.
Functions
- DO use a function declaration to bind a function to a name.
- DON’T create a lambda when a tear-off will do.
Variables
- DON’T explicitly initialize variables to
null
. - AVOID storing what you can calculate.
- CONSIDER omitting the types for local variables.
Members
- DON’T wrap a field in a getter and setter unnecessarily.
- PREFER using a
final
field to make a read-only property. - CONSIDER using
=>
for short members whose body is a single return statement. - DON’T use
this.
when not needed to avoid shadowing. - DO initialize fields at their declaration when possible.
Constructors
- DO use initializing formals when possible.
- DON’T type annotate initializing formals.
- DO use
;
instead of{}
for empty constructor bodies. - DO place the
super()
call last in a constructor initialization list.
Error handling
- AVOID catches without
on
clauses. - DON’T discard errors from catches without
on
clauses. - DO throw objects that implement
Error
only for programmatic errors. - DON’T explicitly catch
Error
or types that implement it. - DO use
rethrow
to rethrow a caught exception.
Asynchrony
Design
Names
- DO use terms consistently.
- AVOID abbreviations.
- PREFER putting the most descriptive noun last.
- CONSIDER making the code read like a sentence.
- PREFER a noun phrase for a non-boolean property or variable.
- PREFER a non-imperative verb phrase for a boolean property or variable.
- CONSIDER omitting the verb for a named boolean parameter.
- PREFER an imperative verb phrase for a function or method whose main purpose is a side effect.
- CONSIDER a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.
- PREFER naming a method
to___()
if it copies the object’s state to a new object. - PREFER naming a method
as___()
if it returns a different representation backed by the original object. - AVOID describing the parameters in the function’s or method’s name.
Libraries
Types
- AVOID defining a one-member abstract class when a simple function will do.
- AVOID defining a class that contains only static members.
- AVOID extending a class that isn’t intended to be subclassed.
- DO document whether your class supports being extended.
- AVOID mixing in a class that isn’t intended to be a mixin.
- DO document whether your class supports being used as a mixin.
Constructors
- PREFER defining constructors instead of static methods to create instances.
- CONSIDER making your constructor
const
if the class supports it.
Members
- PREFER making fields and top-level variables
final
. - DO use getters for operations that conceptually access properties.
- DO use a setter for operations that conceptually change a property.
- DON’T define a setter without a corresponding getter.
- AVOID returning
null
from members whose return type isbool
,double
,int
, ornum
. - AVOID returning
this
from methods just to enable a fluent interface.
Type annotations
- DO type annotate public APIs.
- DON’T specify a return type for a setter.
- PREFER type annotating private declarations.
- AVOID annotating types on function expressions.
- AVOID annotating with
dynamic
when not required. - AVOID annotating with
Function
. - DO annotate with
Object
instead ofdynamic
to indicate any object is accepted.
Parameters
- AVOID positional boolean parameters.
- AVOID optional positional parameters if the user may want to omit earlier parameters.
- AVOID mandatory parameters that permit nonce values.
- DO use inclusive start and exclusive end parameters to accept a range.
Equality