When implementing a new class, test, table or API end point, it is useful to have examples that demonstrate accumulated best practices. Non-functional examples in docs risk obsoletion because they are not functional, tested or in production. Create examples that are tested and functional in production to ensure they are up-to-date. The general rule is add docs to functional example code, do not add broken example code to docs.
An example comprehensively demonstrates the best practices for all use cases. This is so the example can be copied to create new code then unwanted use cases can be removed. Keep examples of every common task.
Below, you will find examples of examples.
/**
* Name classes with a noun (ExampleClass) and a verb (Demonstrator)
*/
class ExampleClassDemonstrator
{/**
* Prefix constant names with the type, e.g. 'MULTIPLIER_'
*/
const MULTIPLIER_DOUBLE = 2;
const MULTIPLIER_TRIPLE = 3;
/**
* Name class instances after their type
*/
private Output $output;
/**
* Use dependency injection with constructor injection being the first preference
*/
public function construct(
Output $output, // use trailing comma
) {$this->output = $output;
}
/**
* Method name comes from class name, reverse the verb (demonstrate) and noun (ExampleClass)
*/
public function demonstrateExampleClass(
int $input1, // one argument per line
int $input2, // use trailing comma
: int {
)$this->output->writeLine('Hello world');
return ($input1 + $input2) * self::MULTIPLIER_DOUBLE; // parantheses reduce calculation errors
} }
use ExampleClassDemonstrator as Subject; // import class under test as 'Subject'
class ExampleClassDemonstratorTest // name test class after class under test
{private Subject $subject; // instance under test is called 'subject'
private Output $output;
public function setup() {
$this->subject = new Subject(
$this->output = mock(Output::class),
)
}
public function testBasicTest () {
// arrange
$input1 = 2;
$input2 = 3;
// act
$actual = this->subject->demonstrateExampleClass( // return value is called 'actual'
$input1,
$input2,
;
)
// assert
$this->assertEquals(
$input1 + $input2) * 2, // demonstrate how complex values are calculated
($actual
;
)
} }
CREATE TABLE `example`
-- table names are singular, not plural
(INT NOT NULL AUTO_INCREMENT,
`id` -- primary key is the first column
TIMESTAMP NOT NULL,
`created_at` -- created_at is the second column
TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`updated_at` -- updated_at is the third column
VARCHAR(255) CHARACTER SET utf16 COLLATE utf16_bin NOT NULL,
`identifier` -- unique string that identifies this row
-- if used, it will be the fourth column
TIMESTAMP NOT NULL,
`permitted_at` -- a boolean recorded as a nullable date to show when it was set
TIMESTAMP,
`found_at` -- use past tense for time-related columns
INT,
`created_by_user_id` -- record who created this row
PRIMARY KEY (`id`),
FOREIGN KEY(created_by_user_id) REFERENCES user(id)
);
An example end point should be functional in production but should have no effect on the system when called. It demonstrates how a new end point is structured and provides an example response.
GET /example?created_at_gte=2022-12-01&is_active=1
# end points are singular, not plural, e.g. 'example' not 'examples'
GET /example
# name comparison filters with gt, gte, lt, lte
created_at_gte=2022-12-01
# use 'is_' for boolean filter names
is_active=1
{
"status": "ok",
"criteria": {
"created_at_gte": "2022-12-01T00:00:00Z",
"is_active": true
}
"total": 2,
"data": [
{},
{}
]
}