First off, thank you for considering contributing to SeedStream! It's people like you that make SeedStream such a great tool.
- Code of Conduct
- Your First Contribution
- Getting Started
- Development Workflow
- Code Quality Standards
- Running Tests
- Pull Request Process
- Style Guide
- Where to Get Help
This project and everyone participating in it is governed by the SeedStream Code of Conduct. By participating, you are expected to uphold this code.
TL;DR: Be respectful, constructive, and welcoming. We're building great software together.
New here? Start with an issue labelled
good first issue.
Many of them touch only YAML config or a single test — no deep Java
needed. The fastest first-PR path:
- Build it green first —
git clone … && cd SeedStream && ./gradlew build. If that passes, your environment is ready. - Pick one
good first issueand comment that you're taking it (so two people don't duplicate work). - Branch —
git checkout -b <type>/<short-description>(e.g.feat/employee-config). - Make the change, then run
./gradlew spotlessApply test— formatting and tests must pass. Most starter issues ask for a test that includes a same-seed determinism check; mirror an existing one. - Open a PR following Pull Request Process. Small, focused PRs get reviewed fastest.
Stuck? Open a draft PR or ask in Discussions — a question is always welcome.
- Java 21 or higher (Amazon Corretto, OpenJDK, or GraalVM)
- Gradle 9.5+ (wrapper included — no system installation required)
- Git for version control
- Docker (optional, for integration tests with Testcontainers)
Recommended: Use SDKMAN! to manage Java and Gradle versions:
# Install SDKMAN! (if not already installed)
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
# Install Java 21
sdk install java 21.0.9-amzn
# Install Gradle (only needed to regenerate the wrapper)
sdk install gradle 9.6.0Once you have the repo cloned, use ./gradlew for everything — the wrapper handles the correct Gradle version automatically.
# Clone repository
git clone https://github.com/mferretti/SeedStream.git
cd SeedStream
# Build project
./gradlew build
# Run tests
./gradlew testBefore making changes, familiarize yourself with:
- DESIGN.md - Architecture and design decisions
- README.md - Features and quick start
- Module structure:
cli → destinations → formats → generators → schema → coreandcli → inspector → schema → core(plus the off-graphbenchmarksJMH module).
The project follows a dependency-first architecture: each module only depends on modules to its right — the arrows above point in the direction of the dependency, so cli depends on destinations, which depends on formats, and so on. core is the leaf with no project dependencies. No circular dependencies are permitted.
# Always branch from main
git checkout main
git pull origin main
# Create feature branch
git checkout -b feature/your-feature-name
# Or bugfix branch
git checkout -b fix/issue-123-descriptionBranch naming conventions:
feature/- New featuresfix/- Bug fixesdocs/- Documentation changesrefactor/- Code refactoringtest/- Test additions or fixes
- Write clean, readable code (see Style Guide)
- Add tests for new functionality
- Update documentation as needed
- Keep commits focused and atomic
Before committing, ensure all checks pass:
# Format code (REQUIRED)
./gradlew spotlessApply
# Run all tests
./gradlew test
# Run static analysis
./gradlew spotbugsMain
# Full build (includes all checks)
./gradlew buildgit add -A
git commit -m "feat: Add support for new data type"Commit message format:
<type>: <subject>
<optional body>
<optional footer>
Types:
feat:- New featurefix:- Bug fixdocs:- Documentation onlystyle:- Code style (formatting, no logic change)refactor:- Code restructuring (no feature change)test:- Adding or updating testschore:- Build process, dependencies, tooling
Examples:
git commit -m "feat: Add Protobuf serializer support"
git commit -m "fix: Correct seed derivation for worker thread 0"
git commit -m "docs: Update README with Kafka configuration examples"git push origin feature/your-feature-nameThen create a Pull Request on GitHub.
SeedStream maintains high code quality standards:
- ✅ Code formatting: Spotless check must pass
- ✅ Test coverage: Maintain or improve coverage (target: 70%+)
- ✅ All tests pass: Unit and integration tests
- ✅ No new SpotBugs warnings: Static analysis clean
- ✅ Documentation updated: If adding features
We use Spotless with Google Java Style Guide (with one exception: opening braces { on same line).
# Check if code is formatted
./gradlew spotlessCheck
# Auto-format code (ALWAYS run before committing)
./gradlew spotlessApplyKey rules:
- Max line length: 120 characters
- Use spaces, not tabs (indent: 2 spaces)
- Braces on same line:
if (condition) {notif (condition)\n{ - No wildcard imports: Use explicit imports (e.g.,
import java.util.List;notimport java.util.*;) - Exception: Static test imports allowed (
import static org.assertj.core.api.Assertions.*;)
Minimum coverage: 70% (enforced by JaCoCo)
# Generate coverage report
./gradlew test jacocoTestReport
# View HTML report
open core/build/reports/jacoco/test/html/index.html
# Verify coverage meets minimum
./gradlew jacocoTestCoverageVerificationTesting guidelines:
- Write tests for all new features
- Test both success and failure scenarios
- Use descriptive test names:
shouldGenerateCorrectDataWhenSeedIsProvided - Use AssertJ for fluent assertions
- Mock external dependencies (use real objects for pure logic)
# Run all unit tests (excludes integration tests)
./gradlew test
# Run tests for specific module
./gradlew :core:test
./gradlew :generators:test
# Run with verbose output
./gradlew test --infoIntegration tests use Testcontainers (requires Docker):
# Run integration tests (takes longer, requires Docker)
./gradlew integrationTest
# Run specific integration test
./gradlew :destinations:integrationTestNote: Integration tests are excluded from regular ./gradlew test to keep the feedback loop fast.
Benchmarks are not run automatically (they take 10-15 minutes):
# Run E2E benchmark suite (preferred)
./benchmarks/run_e2e_test.sh
# Run JMH component benchmarks directly
./gradlew :benchmarks:jmh
python3 benchmarks/format_results.py > BENCHMARK-RESULTS.mdChecklist:
- Code is formatted (
./gradlew spotlessApply) - All tests pass (
./gradlew test) - Coverage is maintained or improved
- Documentation is updated (README, JavaDoc, etc.)
- Commit messages follow convention
- Branch is up to date with
main
Use this template:
## Description
Brief description of what this PR does.
## Type of Change
- [ ] Bug fix (non-breaking change fixing an issue)
- [ ] New feature (non-breaking change adding functionality)
- [ ] Breaking change (fix or feature that breaks existing functionality)
- [ ] Documentation update
## Testing
How has this been tested?
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing performed
## Checklist
- [ ] Code formatted with Spotless
- [ ] All tests pass
- [ ] Coverage maintained (70%+)
- [ ] Documentation updated
- [ ] CHANGELOG.md updated (if applicable)- Automated checks: GitHub Actions runs all tests and checks
- Code review: Maintainer reviews code and provides feedback
- Iteration: Address feedback and push updates
- Approval: Once approved, your PR will be merged
Typical review time: 2-3 days
Your changes will be included in the next release. Thank you for your contribution! 🎉
Follow Google Java Style Guide with these specifics:
- Classes:
PascalCase(e.g.,DataGenerator,KafkaDestination) - Methods:
camelCase(e.g.,generateData(),writeToFile()) - Constants:
UPPER_SNAKE_CASE(e.g.,DEFAULT_BATCH_SIZE) - Packages:
lowercase(e.g.,com.datagenerator.core)
// GOOD ✅
import java.util.List;
import java.util.Map;
import com.datagenerator.core.type.DataType;
// BAD ❌
import java.util.*;
import com.datagenerator.core.type.*;Exception: Static test imports allowed
import static org.assertj.core.api.Assertions.*; // OK for testsUse Lombok to reduce boilerplate:
@Valuefor immutable classes (config objects)@Builderfor classes with 4+ parameters@Slf4jfor logging- Import Lombok classes at top, use simple names in code
// GOOD ✅
import lombok.Value;
import lombok.Builder;
@Value
@Builder
public class Config {
String name;
int count;
}
// BAD ❌
@lombok.Value // Don't use fully-qualified
public class Config { }Return Optional<T> for methods that may not have a value:
// GOOD ✅
public Optional<User> findUserById(String id) {
// ...
}
// BAD ❌
public User findUserById(String id) {
return null; // Never return null
}Never return null collections:
// GOOD ✅
public List<String> getNames() {
return List.of(); // Empty list
}
// BAD ❌
public List<String> getNames() {
return null;
}Use modern Java features:
- Records for simple data carriers
- Pattern matching for instanceof checks
- Switch expressions instead of switch statements
- Text blocks for multi-line strings
- Virtual threads for I/O-bound operations
# Use 2-space indentation
name: address
geolocation: usa
# Use quotes for strings with special characters
alias: "nome"
# Comments for complex configurations
conf:
bootstrap: localhost:9092 # Kafka broker
topic: addresses # Target topicRequired for:
- All public classes
- All public methods
- All public fields/constants
- Complex algorithms
/**
* Generates random data based on a seed value.
*
* <p>This generator ensures reproducible output: the same seed always produces
* identical data across multiple runs, even with multi-threaded generation.
*
* @param seed the seed value for deterministic generation
* @param count the number of records to generate
* @return a list of generated records
* @throws GeneratorException if generation fails
*/
public List<Map<String, Object>> generate(long seed, int count) {
// ...
}Use sparingly - code should be self-documenting. Comment the "why", not the "what":
// GOOD ✅
// Use logical worker IDs instead of JVM thread IDs for reproducibility
int workerId = workerIdCounter.getAndIncrement();
// BAD ❌
// Increment the counter
int workerId = workerIdCounter.getAndIncrement();- Questions? Open a GitHub Discussion
- Bug reports? Open a GitHub Issue
- Feature requests? Open a GitHub Issue with
enhancementlabel - Architecture questions? Read DESIGN.md first, then ask in Discussions
We welcome many types of contributions:
Found a bug? Please include:
- Clear description of the issue
- Steps to reproduce
- Expected vs actual behavior
- Version/commit hash
- Java version and OS
Have an idea? Great! Please include:
- Use case (what problem does it solve?)
- Proposed solution
- Alternatives considered
- Willingness to implement it yourself
Documentation is always appreciated:
- Fix typos or unclear explanations
- Add examples or tutorials
- Improve JavaDoc
- Translate documentation
Want to add a new data generator?
- Implement the
DataGeneratorinterface (generate(Random, DataType)+supports(DataType)) - Add tests (80%+ coverage)
- Update type system documentation
- Add examples in
config/structures/
For a new semantic type, you usually don't need a new generator at all — register it on
DatafakerRegistry, or declare it in a --faker-types YAML (including regex: patterns) with no code.
Want to add a new destination (S3, Azure, Elasticsearch)?
- Implement
DestinationAdapterinterface - Add configuration model (extends
DestinationConfig) - Add comprehensive tests (unit + integration with Testcontainers)
- Update documentation
Want to add a new serialization format (Protobuf, Avro)?
- Implement
FormatSerializerinterface - Add tests for all data types
- Update CLI to support new format
- Document format-specific configuration
By contributing, you agree that your contributions will be licensed under the Apache License 2.0, the same license as SeedStream.
Thank you for making SeedStream better! 🚀
For more details on the project architecture and design decisions, see DESIGN.md.