Developer Workflow Guide

This document establishes the workflow, coding standards, and architectural patterns for this project.

Table of Contents

  1. Philosophy
  2. Project Overview
  3. Argument Ordering Pattern
  4. Method Naming Conventions
  5. Pass-by-Reference Methods
  6. Documentation Standards
  7. Testing Requirements
  8. Code Quality Standards
  9. Adding New Methods
  10. Anti-Patterns to Avoid

Philosophy

The architectural foundation of Phuture rests on a single conviction: every line of code must be explicit, deterministic, and fully traceable in its behavior. There is no place for magic, implicit conventions, or hidden functionality that the developer did not expressly define.

Explicitness Over Convention

Code must declare its intent unambiguously. Behavior that relies on undocumented assumptions, implicit type coercion, global state mutation, or framework-managed lifecycle hooks is categorically rejected. A method must do exactly what its signature and documentation describe — nothing more, nothing less. Side effects must be documented; implicit side effects are defects.

Determinism as a Requirement

Deterministic execution is non-negotiable. Given identical inputs, a method must produce identical outputs across every invocation, runtime environment, and execution context. Reliance on mutable global state, non-seeded randomness in business logic, or undefined ordering in data structures is prohibited unless the method's contract explicitly guarantees otherwise.

No Magic

Magic — defined as any behavior that a developer cannot trace by reading the source code linearly — is an anti-pattern. This includes, but is not limited to: method missing interception, implicit method injection, automatic dependency resolution through naming conventions, runtime code generation that alters program behavior, and configuration-driven behavioral changes that are not statically analyzable. The execution path from invocation to resolution must be traceable through explicit, statically typed code.

Traceability

Every operation must be traceable to its definition. When a developer encounters a method call, they must be able to navigate directly to its implementation without relying on IDE inference over dynamic dispatch, runtime proxies, or convention-based resolution. This principle extends to data flow: the origin and transformation of every value must be discernible through static analysis of the codebase.

Why This Matters

You deserve to know precisely what every line of your code does. This is not merely a matter of readability — it is a prerequisite for true scalability. Systems that rely on implicit behavior accumulate hidden contracts that resist modification, impede onboarding, and produce defects that manifest only under specific conditions. By enforcing explicitness and determinism at every layer, Phuture ensures that growth in codebase size does not correspond to growth in uncertainty. There must be no surprises in the future.


Project Overview

This document defines the universal workflow, coding standards, and architectural patterns to follow across any project.

Key Principles

  • Consistency: All methods must follow the same patterns and conventions
  • Predictability: Method signatures and behavior must be intuitive and uniform
  • Uniformity: Naming conventions and documentation must be standardized
  • Descriptive naming: All methods, variables, constants, and properties must have a clear and descriptive name
    • A name should tell you what it represents without requiring a comment to explain it
  • Self-explanatory code: Code should read like prose, the logic itself communicates its intent
    • Avoid excessive comments; if a comment is needed to explain what the code does, the code should be rewritten instead

Argument Ordering Pattern

CRITICAL: Standard Pattern

All methods MUST follow this argument ordering:

([required], [optional], [variadics])

Detailed Breakdown

1. Required Parameters

Search terms, needle values, patterns, or other required operation data:

1Arrays::search(array $array, mixed $needle, ...)
2Strings::contains(string $subject, string $search, ...)
3Hash::md5(string $data, ...)

2. Optional Parameters

Flags, modes, offsets, encoding, and other optional modifiers:

1Arrays::search(array $array, mixed $needle, bool $strict = false)
2Strings::position(string $subject, string $search, int $offset = 0)
3Hash::md5(string $data, bool $binary = false)

3. Variadic Parameters (ALWAYS LAST)

Variadic parameters using the ... operator must always be the last parameter:

1Arrays::merge(bool $recursive = false, ...$arrays)
2Arrays::difference(array $array, ?callable $callback = null, array ...$arrays)

Method Naming Conventions

CamelCase Naming

All method names use camelCase:

Native PHP Function Project Method Class
array_search() search() Collection
array_key_exists() containsKey() Collection
str_contains() contains() Text
strpos() position() Text
htmlentities() entityEncode() HtmlEncoder
urlencode() encode() UrlEncoder
hash() make() Hasher

Descriptive Names

  • Use clear, descriptive names that convey the method's purpose
  • Avoid abbreviations unless commonly understood
  • Maintain consistency with related methods

Examples:

  • Collection::sort() - Simple sort
  • Collection::sortByKey() - Sort by keys
  • Collection::sortWithIndex() - Sort maintaining index association
  • Text::toCamelCase() - Convert to camelCase
  • Text::toSnakeCase() - Convert to snake_case

Pass-by-Reference Methods

When to Use Pass-by-Reference

Methods that modify the original data structure must use pass-by-reference:

1public static function sort(array &$array, int $flags = SORT_REGULAR): bool
2public static function push(array &$array, mixed ...$values): int
3public static function shift(array &$array): mixed

Documentation Requirements

Always clearly document pass-by-reference in PHPDoc:

 1/**
 2 * Sorts an array in ascending order.
 3 *
 4 * Modifies the original array passed by reference.
 5 *
 6 * @param array &$array The array to sort (passed by reference)
 7 * @param int $flags Sorting type flags (default: SORT_REGULAR)
 8 * @return bool Returns true on success, false on failure
 9 */
10public static function sort(array &$array, int $flags = SORT_REGULAR): bool
11{
12    return sort($array, $flags);
13}

Documentation Standards

Method Documentation

Required PHPDoc Format

Every method MUST include complete PHPDoc with the following structure:

 1/**
 2 * Brief one-line description of what the method does.
 3 *
 4 * Extended description explaining what this method does in simple, clear language.
 5 * Avoid complex technical terms. If you must use a technical term, explain what it means.
 6 * Describe the behavior in a way that anyone can understand.
 7 *
 8 * Example:
 9 * ```php
10 * use NameSpace\ClassName;
11 * 
12 * $result = ClassName::methodName($data, $param);
13 * 
14 * // Returns: [1, 2, 3]
15 * ```
16 *
17 * @param type $param Clear description of what this parameter is and what it's used for
18 * @param type $param Clear description (mention default values if applicable)
19 * @return type Clear description of what gets returned and what it means
20 */

PHPDoc Requirements

  1. Brief Description: One-line summary in simple language
  2. Extended Description: Detailed explanation using everyday words (required)
  3. Example: Always include a short usage example wrapped in ```php code blocks
  4. Other Classes: When referring to other classes, always use FQCNs
  5. @param Tags: For EVERY parameter with clear, simple descriptions
  6. @return Tag: Clear description of what is returned and what it represents, omit when void
  7. Callback Documentation: When a parameter requires a callback function, the @param description MUST include the callback signature in the format: The callback has the signature `function (mixed $value): mixed`
  8. Additional Tags (when applicable):
    • @see - For methods that have other related methods, like first() being related to last(), or flatten() to unflatten(), always use FQCNs
    • @throws - For methods that throw exceptions, always use FQCNs
    • @deprecated - For deprecated methods

Documentation Best Practices

  • Use simple, clear language - Write as if explaining to someone new to programming
  • Avoid jargon - Don't use technical terms unless necessary
  • Explain technical terms - If you must use a complex term, explain it immediately
  • Include examples - Every method must have at least one usage example
  • Be specific - Instead of "processes data", say "converts text to lowercase"
  • Mention defaults - Document default parameter values in plain English
  • Describe edge cases - Explain what happens with empty inputs or special cases

Examples of Good Documentation

Example 1: Simple Method

 1/**
 2 * Checks if a value exists anywhere in an array.
 3 *
 4 * This method searches through an array to see if a specific value is present.
 5 * Think of it like looking through a list to find a specific item.
 6 *
 7 * Example:
 8 * ```php
 9 * use App\Utils\Collection;
10 * 
11 * $fruits = ['apple', 'banana', 'orange'];
12 * $hasApple = Collection::contains($fruits, 'apple');
13 * 
14 * // Returns true
15 * ```
16 *
17 * @param array $array The list of items to search through
18 * @param mixed $value The item you're looking for
19 * @param bool $strict If true, checks both value and type (default: false)
20 * @return bool Returns true if found, false if not found
21 */
22public static function contains(array $array, mixed $value, bool $strict = false): bool
23{
24    ...
25}

Example 2: Method with Callback Parameter

 1/**
 2 * Applies a filter to all values in a nested array.
 3 *
 4 * This method goes through every item in an array, including items inside nested arrays
 5 * (arrays within arrays), and applies a transformation to each value. A "callback" is a
 6 * function you provide that tells this method how to transform each value.
 7 *
 8 * For example, you could use this to sanitize (clean) all user input in a complex form,
 9 * or convert all values to uppercase.
10 *
11 * Example:
12 * ```php
13 * use App\Utils\Collection;
14 *
15 * $data = ['name' => 'John', 'address' => ['city' => 'NYC', 'zip' => '10001']];
16 * $clean = Collection::filterRecursive($data, fn($val) => htmlspecialchars($val));
17 * ```
18 *
19 * @param array $array The array to process, which may contain nested arrays
20 * @param callable $callback A function that receives each value and returns the transformed value.
21 *  The callback has the signature `function (mixed $value): mixed`
22 * @return array Returns a new array with all values transformed by your callback function
23 */
24public static function filterRecursive(array $array, callable $callback): array
25{
26    ...
27}

Example 3: Method That Modifies Original Data

 1/**
 2 * Sorts an array in alphabetical or numerical order.
 3 *
 4 * This method arranges the items in an array from smallest to largest (or A to Z).
 5 * IMPORTANT: This changes the original array that you pass in - it doesn't create a copy.
 6 *
 7 * Example:
 8 * ```php
 9 * use App\Utils\Collection;
10 * 
11 * $numbers = [3, 1, 4, 1, 5];
12 * Collection::sort($numbers);
13 * 
14 * // $numbers is now [1, 1, 3, 4, 5]
15 * ```
16 *
17 * @param array $array The array to sort (this will be modified directly)
18 * @param int $flags How to compare items - use SORT_REGULAR for normal sorting (default: SORT_REGULAR)
19 * @return bool Returns true if sorting succeeded, false if it failed
20 */
21public static function sort(array &$array, int $flags = SORT_REGULAR): bool
22{
23    ...
24}

Bad Examples vs. Good Examples

Bad: Too technical and unclear

1/**
2 * Performs a binary search on a sorted array using a comparison function.
3 *
4 * @param array $array The haystack
5 * @param mixed $needle The value to locate
6 * @return int|false The index or false
7 */

Good: Clear and simple

 1/**
 2 * Finds the position of a value in an array.
 3 *
 4 * Searches through an array to find where a specific value is located.
 5 * Returns the position number (starting from 0) if found.
 6 *
 7 * Example:
 8 * ```php
 9 * $colors = ['red', 'blue', 'green'];
10 * $position = Collection::search($colors, 'blue'); // Returns 1
11 * ```
12 *
13 * @param array $array The array to search through
14 * @param mixed $needle The value you're looking for
15 * @param bool $strict If true, also checks that the type matches (default: false)
16 * @return int|string|false Returns the position if found, or false if not found
17 */

Property Documentation

Every class property and class constant MUST be documented with a multiline PHPDoc block. Single-line inline doc comments (/** @var … */) are not allowed.

Required format:

1/**
2 * Brief one-line description of what the property holds.
3 *
4 * Extended description providing context: what the structure looks like,
5 * how it is keyed, when it is populated, and any invariants that apply.
6 *
7 * @var type
8 */
9private array $propertyName = [];

Good example:

1/**
2 * Cached results keyed by the input string that produced them.
3 *
4 * Populated on first access and reused on subsequent calls with the same
5 * input. Cleared whenever the underlying data source changes.
6 *
7 * @var array
8 */
9private array $cache = [];

Bad example (not allowed):

1/** @var int Maximum number of retry attempts before giving up */
2public const MAX_RETRIES = 3;

Property PHPDoc Requirements

  1. Brief description: One-line summary on the opening line of the block
  2. Extended description: At least one sentence explaining what the property stores, how it is structured, or when it changes — skip only if the property is completely self-evident from its name and type alone
  3. @var tag: Always present on its own line; include the most specific type possible

Testing Requirements

MANDATORY RULE

Every new method MUST have corresponding tests in ./tests using Nette Tester.

A method is NOT considered complete until its tests are written and passing.

Testing Framework

  • Framework: Nette Tester
  • Test Directory: ./tests/
  • Test File Naming: Match class name (e.g., ArraysTest.php for Arrays.php)
  • Test Method Naming: test{MethodName}() pattern

Test Coverage Requirements

Tests must cover:

  1. Happy path: Normal expected usage
  2. Edge cases: Boundary conditions, empty inputs, null values
  3. Error conditions: Invalid inputs, expected exceptions
  4. All code paths: Every branch and condition in the method

Running Tests

1# Run all tests
2composer test
3
4# Run specific test file
5composer test tests/ArraysTest.php

Test Example

 1<?php
 2
 3namespace App\Tests\Utils;
 4
 5use App\Utils\Collection;
 6use Tester\Assert;
 7use Tester\TestCase;
 8
 9require __DIR__ . '/bootstrap.php';
10
11class CollectionTest extends TestCase
12{
13    public function testContains(): void
14    {
15        // Happy path
16        Assert::true(Collection::contains([1, 2, 3], 2));
17        Assert::false(Collection::contains([1, 2, 3], 4));
18
19        // Strict comparison
20        Assert::true(Collection::contains([1, 2, '3'], 3, false));
21        Assert::false(Collection::contains([1, 2, '3'], 3, true));
22
23        // Edge cases
24        Assert::false(Collection::contains([], 1));
25        Assert::true(Collection::contains([null], null));
26    }
27}
28
29(new CollectionTest())->run();

Code Quality Standards

Required Standards

  1. PSR-12: Mandatory coding style standard
  2. PHPStan: Static analysis at level 6
  3. PHP CodeSniffer: Automatic PSR-12 enforcement

Quality Check Commands

1# Auto-fix PSR-12 violations
2composer lint
3
4# Run all quality checks
5composer test

Pre-Commit Checklist

Before committing code, ensure:

  • All tests pass
  • PHPStan passes with no errors
  • Code follows PSR-12
  • All new methods have tests
  • All methods have complete PHPDoc

Adding New Methods

Step-by-Step Checklist

  1. Determine Primary Data Parameter

    • Identify what data the method operates on
    • This becomes the first parameter
  2. Order Arguments According to Pattern

    • ([required], [optional], [variadics])
    • Follow the standard pattern strictly
  3. Add Comprehensive PHPDoc

    • Brief description in simple language
    • Extended description explaining clearly (avoid jargon)
    • Usage example wrapped in ```php code blocks (REQUIRED)
    • All @param tags with clear descriptions
    • @return tag explaining what is returned
    • @see tag linking other related methods, like first() being related to last(), or flatten() to unflatten()
  4. Implement Method Body

    • Keep it simple and focused
    • Add type safety where beneficial
  5. Write Tests (MANDATORY)

    • Create tests in ./tests/ using Nette Tester
    • Cover all code paths and edge cases
    • Method is incomplete without tests
  6. Run Tests

    • Execute composer test
    • Verify all tests pass
    • Check test coverage if needed

Example: Adding a New Method

1public static function containsKey(array $array, string|int $key): bool
2{
3    return array_key_exists($key, $array);
4}

Corresponding Test:

1public function testContainsKey(): void
2{
3    $array = ['foo' => 'bar', 'baz' => 'qux'];
4
5    Assert::true(Collection::containsKey($array, 'foo'));
6    Assert::false(Collection::containsKey($array, 'nonexistent'));
7    Assert::true(Collection::containsKey([0 => 'a', 1 => 'b'], 0));
8    Assert::false(Collection::containsKey([], 'key'));
9}

Anti-Patterns to Avoid

Wrong Argument Ordering

1// WRONG: Optional parameter before required parameter
2public static function search(array $array, bool $strict = false, mixed $needle)
3
4// WRONG: Variadic not last
5public static function merge(array ...$arrays, bool $recursive = false)
6
7// WRONG: Primary data not first
8public static function contains(mixed $needle, array $array)

Missing PHPDoc

1// WRONG: No documentation
2public static function search(array $array, mixed $needle, bool $strict = false)
3{
4    return array_search($needle, $array, $strict);
5}

Incomplete PHPDoc

1// WRONG: Missing return and parameter descriptions
2/**
3 * Searches for a value in an array.
4 */
5public static function search(array $array, mixed $needle, bool $strict = false)

Inconsistent Naming

1// WRONG: Using snake_case instead of camelCase
2public static function array_search(array $array, mixed $needle): int|false
3
4// WRONG: Not descriptive enough
5public static function srch(array $array, mixed $needle): int|false

Cryptic Variable and Method Names

1// WRONG: Single letters and abbreviations reveal nothing
2$r = new \ReflectionFunction($cb);
3$cls = $r->getClosureScopeClass()?->name;
4$obj = $r->getClosureThis();
5
6// RIGHT: Names tell the story without a comment
7$reflection = new \ReflectionFunction($callback);
8$scopeClass = $reflection->getClosureScopeClass()?->name;
9$boundObject = $reflection->getClosureThis();

Inline Property Doc Instead of Multiline PHPDoc Block

 1// WRONG: Single-line inline PHPDoc for a property or constant
 2/** @var array List of open handles */
 3private array $handles = [];
 4
 5// RIGHT: Multiline PHPDoc block
 6/**
 7 * List of open resource handles keyed by identifier.
 8 *
 9 * @var array
10 */
11private array $handles = [];

Comments That Explain "What" Instead of Rewriting the Code

 1// WRONG: Comment compensates for a bad name
 2$f = true; // flag indicating the loop should stop
 3foreach ($items as $item) {
 4    if ($f) { ... }
 5}
 6
 7// RIGHT: The code speaks for itself
 8$shouldStopProcessing = true;
 9foreach ($items as $item) {
10    if ($shouldStopProcessing) { ... }
11}

Summary

This workflow guide establishes the standards for maintaining consistency, predictability, and quality across any project. By following these patterns and practices, we ensure that the codebase remains:

  • Consistent: All methods follow the same conventions
  • Readable: Code reads like prose — self-explanatory without excessive comments
  • Maintainable: Clear documentation and tests make updates easy
  • Reliable: Comprehensive testing and quality checks prevent regressions

Remember the key principles:

  1. Classes only in ./src/
  2. Argument ordering: ([required], [optional], [variadics])
  3. Descriptive names for every method, variable, constant, and property — clear intent, no abbreviations
  4. Self-explanatory code — if a comment explains what the code does, rewrite the code instead
  5. Complete PHPDoc for every method
  6. Tests are mandatory for every new method
  7. PSR-12 compliance enforced through tooling

When in doubt, refer to existing classes as examples of proper implementation.