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.
@Change
annotation: Defines the Change's unique identifier (id
) and author for accountability.@TargetSystem
annotation: 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:
NNNN
with 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