Change Anatomy & Structure
Every Change is configured through key components that work together, following the natural development workflow:
- File name: Created first, determines execution order and change name.
@Changeannotation: Defines the Change's unique identifier (id) and author for accountability.@TargetSystemannotation: Specifies which system the Change will affect.- Apply and rollback methods: Implement the actual change logic.
Understanding this anatomy is essential for creating reliable changes that execute predictably and safely.
Complete example
Here's what a complete Change looks like with minimal configuration - we'll explain each component in detail below:
@TargetSystem("user-database")
@Change(id = "add-user-status", author = "backend-team")
public class _0001__AddUserStatus {
@Apply
public void apply(MongoDatabase database) {
database.getCollection("users")
.updateMany(
new Document("status", new Document("$exists", false)),
new Document("$set", new Document("status", "active"))
);
}
@Rollback
public void rollback(MongoDatabase database) {
database.getCollection("users")
.updateMany(
new Document(),
new Document("$unset", new Document("status", ""))
);
}
}
Now let's break down each component:
File name and order
The execution order of Changes is determined by the filename pattern. Every Change file must follow the _ORDER__CHANGE-NAME format.
File naming pattern
Pattern: _ORDER__CHANGE-NAME.[java|yaml]
- ORDER: Alphanumeric string (required: at least 4 characters, left-padded zeros)
- Double underscore
__: Separates order from change name - CHANGE-NAME: PascalCase descriptive name
Examples:
_0001__CreateInvoiceCollection.java
_0002__AddUserStatusColumn.yaml
_0003__MigrateUserData.java
_0010__OptimizeQueries.java
How Flamingock extracts order
Flamingock uses a simple rule to determine execution order:
- Filename must start with underscore
_ - Everything between the first
_and__(double underscore) becomes the order - Everything after
__is the descriptive name
Name examples:
| Filename | Extracted Order | Change Name |
|---|---|---|
_0001__CreateUsers.java | 0001 | CreateUsers |
_0010__SimpleChange.yaml | 0010 | SimpleChange |
_V1_2__DatabaseUpgrade.java | V1_2 | DatabaseUpgrade |
Order rules:
- Required: Orders must be at least 4 characters (compilation requirement)
- Recommended format:
NNNNwith left-padding zeros (e.g.,0001,0002,0010) - Flexibility: Can contain any characters valid for OS filenames and Java class names
- Evaluation: Orders are compared alphanumerically for execution sequence
- Immutability: Cannot be changed once deployed
Change annotation properties
The @Change annotation must define these two properties, with optional properties available (covered later):
id - Unique identifier
The id must be unique across all Changes in your application.
@Change(id = "add-user-status", author = "dev-team")
Rules:
- Must be unique application-wide
- Use descriptive names (e.g.,
add-user-status, notchange1) - Cannot be modified once deployed
author - Responsibility tracking
Identifies who is responsible for this change.
@Change(id = "update-schema", author = "database-team")
@Change(id = "migrate-users", author = "john.doe@company.com")
Best practices:
- Use team names for shared responsibility:
database-team,api-team - Use individual emails for personal changes:
john.doe@company.com - Keep consistent within your organization
Target system annotation
@TargetSystem - System specification
Declares which target system this Change affects.
@TargetSystem("user-database")
@Change(id = "add-user-fields", author = "api-team")
public class _0001__AddUserFields {
// Implementation
}
Apply and rollback methods
Both methods implement your change logic and automatically receive the dependencies they need through parameters.
@Apply - Change logic
Contains the actual change implementation.
@Apply
public void apply(S3Client s3) {
// Your change logic here
s3.putBucketPolicy(/* configure bucket */);
}
@Rollback - Undo logic
Provides logic to reverse the change, essential for safety and CLI undo operations.
@Rollback
public void rollback(S3Client s3) {
// Undo the change
s3.deleteBucketPolicy(/* remove configuration */);
}
Why rollback is required:
- Executed automatically on failure for non-transactional systems
- Required for CLI/UI undo operations
- Ensures every change can be reversed
For detailed information about method parameters, dependency injection, and advanced parameter features, see Apply and rollback methods.
Optional properties
The @Change annotation supports additional optional properties to control behavior:
transactional - Transaction behavior
Controls whether the change runs within a transaction (default: true).
@Change(
id = "create-large-index",
author = "db-team",
transactional = false // DDL operations may require this
)
Important: For non-transactional target systems (S3, Kafka, etc.), this flag has no effect.
recovery - Failure handling strategy
Controls how Flamingock handles execution failures (default: MANUAL_INTERVENTION).
// Default behavior (manual intervention)
@Change(id = "critical-change", author = "team")
public class _0001__CriticalChange {
// Execution stops on failure, requires manual resolution
}
// Automatic retry
@Recovery(strategy = RecoveryStrategy.ALWAYS_RETRY)
@Change(id = "idempotent-change", author = "team")
public class _0002__IdempotentChange {
// Automatically retries on failure until successful
}
Recovery strategies:
MANUAL_INTERVENTION(default): Stops execution on failure, requires CLI resolutionALWAYS_RETRY: Automatically retries on subsequent executions until successful
For detailed information on recovery strategies, see Safety and Recovery.
Next steps
- Change types & implementation - Deep dive into code-based vs template-based approaches
- Change best practices - Learn proven patterns for reliable Changes
- Target systems - Configure where your changes will be applied