Skip to main content

Application Configuration

The configuration system in this project is built around a class-based hierarchy using Python's dataclasses. This approach provides a structured way to manage environment-specific settings, such as cache tuning, security keys, and pagination limits, while ensuring that common defaults are shared across development, testing, and production environments.

Configuration Strategy

The application uses a base configuration class, BaseConfig, which defines the default settings for all environments. Specific environments then inherit from this class and override values as needed. These classes are defined in app/config.py.

The primary configuration classes are:

  • BaseConfig: The foundation for all settings.
  • DevelopmentConfig: Optimized for local development with debugging enabled.
  • ProductionConfig: A strict configuration for live deployments.
  • TestingConfig: Tailored for running automated tests.

Base Configuration

The BaseConfig class in app/config.py establishes the baseline for the application. It includes settings for security, debugging, and pagination.

@dataclass
class BaseConfig:
"""Base configuration shared across all environments."""

SECRET_KEY: str = field(default_factory=lambda: os.environ.get("SECRET_KEY", "change-me"))
DEBUG: bool = False
TESTING: bool = False
PAGE_SIZE: int = DEFAULT_PAGE_SIZE

def get_cache_config(self) -> Dict[str, Any]:
"""Return cache settings for this environment."""
return _build_cache_config()

def _validate(self) -> bool:
"""Check internal invariants. Not part of the public API."""
return bool(self.SECRET_KEY) and self.PAGE_SIZE <= MAX_PAGE_SIZE

The _validate() method is an internal check to ensure that the SECRET_KEY is present and that the PAGE_SIZE does not exceed the MAX_PAGE_SIZE (set to 100).

Environment-Specific Settings

Development and Testing

For local development, DevelopmentConfig enables DEBUG mode and uses a smaller PAGE_SIZE (10) to make it easier to test pagination logic. It also configures a short-lived cache (30s TTL, 128 entries) to ensure that changes are reflected quickly.

@dataclass
class DevelopmentConfig(BaseConfig):
"""Configuration for local development."""

DEBUG: bool = True
PAGE_SIZE: int = 10

def get_cache_config(self) -> Dict[str, Any]:
return _build_cache_config(ttl=30, max_size=128)

TestingConfig sets TESTING=True and uses an even smaller PAGE_SIZE (5) to exercise pagination logic rapidly during test runs.

Production Readiness

The ProductionConfig class enforces stricter requirements for live environments. Most notably, it makes the SECRET_KEY mandatory by attempting to access it directly from os.environ, which will raise a KeyError if the environment variable is missing.

@dataclass
class ProductionConfig(BaseConfig):
"""Configuration for production deployments."""

SECRET_KEY: str = field(default_factory=lambda: os.environ["SECRET_KEY"])
PAGE_SIZE: int = DEFAULT_PAGE_SIZE

def get_cache_config(self) -> Dict[str, Any]:
return _build_cache_config(ttl=600, max_size=4096)

In production, the cache is tuned for performance with a longer TTL (600s) and a larger capacity (4096 entries).

Cache Configuration

The application uses an internal helper, _build_cache_config, to generate a dictionary of cache settings. This dictionary includes ttl_seconds, max_entries, and the eviction policy (defaulting to lru).

While the configuration classes define these settings via get_cache_config(), it is important to note a current implementation detail in app/services/bookmark_service.py. The BookmarkService currently hardcodes its internal LRUCache size to 256 in its _init_services() method, bypassing the values defined in the configuration classes:

# app/services/bookmark_service.py
def _init_services(self) -> None:
"""Bootstrap repository, cache, and search index."""
self._repo = BookmarkRepository()
self._cache: LRUCache[Bookmark] = LRUCache(max_size=256) # Hardcoded value
self._search = SearchIndex(self._repo)

Pagination Constants

The application defines public constants in app/config.py to govern API response sizes:

  • DEFAULT_PAGE_SIZE: 25
  • MAX_PAGE_SIZE: 100

These constants are intended to be used across the application to maintain consistency. For example, the list_bookmarks route in app/routes/bookmarks.py uses 25 as the default per_page value for its query parameters.

Application Factory Integration

The Flask application factory, create_app, located in app/__init__.py, accepts a config_class argument. It uses Flask's app.config.from_object() method to load the settings from the provided class.

def create_app(config_class=DevelopmentConfig) -> Flask:
"""Application factory."""
app = Flask(__name__)
app.config.from_object(config_class)
# ... registration of blueprints ...
return app

By default, the application initializes with DevelopmentConfig if no other class is specified, as seen in the entry point run.py. To run the application with a different configuration, such as in a production environment, you would pass ProductionConfig to the factory.