Application Decisions
Avoid Class-Level State in Concurrent Environments
Class variables and class-level state introduce significant risk in multi-threaded or multi-request environments. Because class variables are shared across all instances of a class, modifying them affects every request, every thread, and every user. This frequently leads to race conditions, unpredictable behavior, and cross-request contamination.
Consider the following example:
def update_without_logging(resource_id)
Audit.disable_logging
@resource = Audit.find(resource_id)
@resource.update(some_field: 'some value')
Audit.enable_logging
endWhy this is dangerous
Audit.disable_logging changes a shared class-level flag.
If multiple requests are being processed concurrently:
- Request A disables logging
- Request B begins at the same time → logging is now disabled for B
- Request A finishes and re-enables logging
- Request B may still assume logging should be disabled
- Or another request may unintentionally run with logging disabled
This pattern results in:
- Non-deterministic logging behavior
- Cross-request side effects
- Race conditions
- Bugs that are extremely difficult to reproduce
This happens because class variables:
- are global to the class
- live for the lifetime of the process
- are not garbage-collected
- are shared between all threads and all requests
Correct Approach: Use Instance-Level State
Instead of modifying shared global state, state should always be scoped to the specific object or request being processed.
For example:
def update_without_logging(resource_id)
@resource = Audit.find(resource_id)
@resource.disable_logging
@resource.update(some_field: 'some value')
endHere:
- Logging behavior applies only to the specific resource instance
- No other request is affected
- No shared state is involved
- No race conditions are possible
Summary
To avoid hard-to-debug concurrency issues:
- Never use class variables or class-level flags to control request-specific behavior
- Always prefer instance variables or request-scoped state
- Avoid global switches or mutable class-level configuration
- Design for concurrency by default
Following these principles ensures your application remains safe, predictable, and correct under load.
