This guide outlines the principles and standards for Streak documentation.
- Explain what a feature is and why it exists
- Focus on architectural decisions and trade-offs
- Show minimal, complete code examples
- Link to tutorials for implementation details
- Show how to implement features step by step
- Start with minimal working examples
- Add complexity gradually with clear reasoning
- Reference core concepts for deeper understanding
- Present as standalone guides, not as part of a series
- Each tutorial should be self-contained and complete
- Begin with the simplest working implementation
- Add one concept at a time
- Ensure each step is complete and functional
- Explain the problem each addition solves
- Show why new complexity is necessary
When introducing a new concept, collapse or omit previously shown code to maintain focus on the new material.
Example of progressive complexity:
Step 1 - Core functionality:
final class Project implements AggregateRoot
{
private string $id;
private string $name;
public static function create(string $id, string $name): self
{
$project = new self();
$project->apply(new ProjectCreated($id, $name));
return $project;
}
protected function applyProjectCreated(ProjectCreated $event): void
{
$this->id = $event->id;
$this->name = $event->name;
}
}Step 2 - Adding input validation:
final class Project implements AggregateRoot
{
// ... previous properties ...
public static function create(string $id, string $name): self
{
if (empty($name)) {
throw new InvalidArgumentException('Project name cannot be empty');
}
$project = new self();
$project->apply(new ProjectCreated($id, $name));
return $project;
}
// ... previous event handling ...
}Step 3 - Adding state modification:
final class Project implements AggregateRoot
{
// ... previous properties ...
public function rename(string $newName): void
{
if (empty($newName)) {
throw new InvalidArgumentException('Project name cannot be empty');
}
$this->apply(new ProjectRenamed($this->id, $newName));
}
// ... previous methods ...
}- Write for experienced software engineers
- Assume knowledge of:
- Object-oriented programming
- Design patterns
- Testing practices
- Don't assume knowledge of:
- Streak internals
- Specific frameworks
- Implementation details
// DO: Complete class with context
<?php
namespace Domain\Example;
use Streak\Domain\Command;
use Streak\Domain\Exception\ValidationFailed;
final class CreateExample implements Command
{
public function __construct(
private string $id,
private string $name
) {
if (empty($name)) {
throw new ValidationFailed('Name cannot be empty');
}
}
public function id(): string
{
return $this->id;
}
}
// DON'T: Methods without context
public function handle($command): void
{
$this->process($command); // Where is process defined?
$this->save(); // What's being saved?
}- Include complete namespace and imports
- Show full class context
- Keep examples focused and minimal
- Use real interfaces from the codebase
- Avoid duplicating code
- Ensure examples are accurate and reflect current best practices
- Avoid showing implementation details that belong in tutorials
- Always consult Streak source files when using Streak code in examples
- Check actual interface definitions
- Verify method signatures and return types
- Use correct namespace paths
- Match implementation details exactly
- Example: If showing
EventStoreusage, checkstreak/src/Domain/EventStore.php
- Use Streak's actual interfaces and implementation patterns
- DO: Implement Streak interfaces like
Query,Command,QueryHandler, etc. - DO: Use Streak's method naming conventions (e.g.,
handle()instead of__invoke()) - DO: Follow Streak's patterns for query handling, command handling, and event processing
- DON'T: Use generic implementations that don't reflect Streak's actual approach
- Example: Query handlers should implement
Streak\Application\QueryHandlerand usehandle()method
- DO: Implement Streak interfaces like
- Avoid obvious code comments
- DON'T:
// Constructor: Initialize with ID and dependencies - DON'T:
// Clock for timestamps (injected) - DON'T:
// Save changes to repository - DO: Only comment when explaining non-obvious behavior or important business rules
- DO: Let the code be self-documenting through clear naming and structure
- DON'T:
- Use PHPDoc type hints when necessary
- DO: Add
/** @var Type $variable */when type information cannot be inferred from context - DO: Include type hints in code examples that show variable usage without full class context
- DO: Use type hints to clarify relationships between objects in examples
- Example:
/** @var Repository $repository */in repository usage examples - Example:
/** @var Project $project */when showing aggregate manipulation
- DO: Add
- Overview - What and Why
- Core Concepts - Key principles
- Basic Usage - Simple examples
- Advanced Topics - Complex scenarios
- Related Concepts - Links and references
- Purpose of each pattern
- Component interactions
- System boundaries
- Consistency guarantees
- Failure handling
- Component interfaces
- Transaction boundaries
- Error handling
- Cross-component communication
- Start simple, add complexity gradually
- Show complete, working examples
- Link related concepts
- Document trade-offs
- Define clear boundaries
- Keep tutorials as standalone guides
- Ensure each document has a clear, focused purpose
- Avoid creating artificial dependencies between tutorials
- Mix concepts with implementation
- Show incomplete code
- Duplicate information
- Oversimplify with analogies
- Include internal details in concepts
- Present tutorials as part of a series unless explicitly intended
- State the obvious or explain natural progressions
- BAD: "As commands grow, they may need more data"
- BAD: "Over time, you might want to add more validation"
- BAD: "As your application grows..."
- Add examples that don't provide additional value
- Group related concepts together
- Provide clear, concise descriptions for each document
- Avoid implying a required reading order unless necessary
- Present tutorials as standalone guides with their own value
- Use relative links between documents
- Reference specific sections when appropriate
- Ensure links are accurate and maintained
- When encountering a broken link, find relevant existing documentation to link to rather than creating a new file
- Only create new files when the concept genuinely requires new documentation
- When linking to a specific topic, prefer linking to a specific section anchor (e.g.,
file.md#section-name) rather than just the file - Create appropriate section headings (using
##or###) to establish anchor points for important concepts - Section anchors in Markdown follow the pattern: lowercase text with spaces replaced by hyphens (e.g.,
## My Sectionbecomes#my-section)