Every enterprise analytics platform sooner or later faces the same challenge. More content is generated than removed. More dashboards are created. More data sources are uploaded. More workflows are launched. Projects grow. Teams evolve. People move on. But nothing much actually gets removed.
In small-scale settings, this poses little operational problem. In enterprise-scale environments, however, things become challenging quite quickly. Ineffective use of disk space becomes an issue. Scheduled refreshes continue running for weeks or months after being no longer needed. Backups continue consuming increasing amounts of resources. Performance gradually worsens.
Cleaning up seems the obvious way out. Automated deletion of unused content becomes the tempting solution.
Except...
What if something important ends up being accidentally deleted? Thousands of users will be affected, and their jobs will become a lot harder overnight.
That was the problem our engineering team had to face while working with a large analytics platform featuring 14 servers and hundreds of thousands of users. Neither manual nor fully automated content cleanups were viable options for us. So, the solution we had to design combined manual validation and automation to ensure safety while achieving operational benefits.
Most organizations tackle content cleanup through one of two approaches.
The first involves manual reviews. Admins check which content is no longer being used and remove it from the platform. This approach works perfectly well in small-scale environments. It's totally impractical in enterprise-scale settings where the number of assets is too large for humans to process manually.
The second approach relies on automated deletion based on inactivity criteria. As long as content remains unused for a specified period, it gets removed automatically.
Unfortunately, different kinds of content behave very differently. Some files get used every day. Others get accessed only once or twice per month. Some are only needed quarterly or annually. Others may appear inactive but still participate in scheduled refreshes or business processes.
As we explored both approaches, we concluded that neither solved the problem effectively. Manual review was operationally impossible. Automated deletion introduced too much risk.
The challenge wasn't identifying stale content. The challenge was doing so safely.
The principle that ultimately guided the architecture was simple:
Don't delete anything you can't restore.
This requirement shaped every decision that followed.
Traditional cleanup automation typically looks like this:
Identify Stale Content
↓
Delete Content
Our system followed a different path:

By separating quarantine from deletion, we fundamentally changed the system's failure modes. If content was incorrectly classified, it could still be recovered. If a stakeholder realized an asset was still needed, intervention remained possible. The system was intentionally engineered so that its default failure mode was inconvenience rather than data loss.
The first major component of the platform was the content discovery engine.
Its objective was straightforward:
Identify content that had not been used within a specific period.
At first glance, this sounds simple.
Most platforms expose metadata such as:
def fetch_all_assets(connections):
"""Iterate server connections, join asset + ownership data."""
all_assets = []
query = """
SELECT a.asset_id, a.last_modified, a.last_accessed,
a.refresh_success_rate, u.user_id AS owner_id, u.is_active AS owner_active
FROM assets a
LEFT JOIN users u ON a.owner_id = u.user_id
WHERE a.deleted_at IS NULL
"""
for conn_info in connections:
try:
with psycopg2.connect(**conn_info) as conn, conn.cursor() as cur:
cur.execute(query)
for row in cur.fetchall():
all_assets.append(dict(zip(
["asset_id", "last_modified", "last_accessed",
"refresh_success_rate", "owner_id", "owner_active"],
row
)))
except Exception as e:
logger.error(f"Skipping {conn_info['host']}: {e}") # one bad server shouldn't halt the run
return all_assets
However, reality proved more complicated. Certain content types generated incomplete usage data. Embedded dashboards sometimes produced no meaningful activity signals. Some assets were accessed indirectly through external applications. Others participated in automated processes without receiving direct user traffic.
As a result, we had to move beyond simple metadata checks and evaluate multiple signals simultaneously.
The platform incorporated factors such as:
def staleness_score(asset):
score = 0
score += min(asset.days_since_last_access / 90, 1.0) * 0.35
score += (1 - asset.refresh_success_rate) * 0.25
score += (0 if asset.embedded_usage_detected else 1) * 0.20
score += (1 if asset.owner_inactive else 0) * 0.20
return score # 0 = active, 1 = strong stale candidate
Rather than relying on a single field, content activity became a composite score built from multiple indicators.
Another challenge emerged early in development. Initially, we considered applying a universal inactivity threshold to all content. That quickly proved problematic.
Different environments operate under different business rhythms. Development environments tend to have short lifecycles. Financial reporting environments often follow quarterly cycles. Compliance-related assets may only be accessed once per year. Applying the same threshold everywhere creates unnecessary risk.
To address this, we introduced configurable retention policies. Retention could vary based on:
|
Environment type |
Inactivity threshold before quarantine |
Grace period |
|---|---|---|
|
Development/sandbox |
~30 days |
3 days |
|
Standard reporting |
~90 days |
14 days |
|
Financial/quarterly cycles |
~120 days |
30 days |
|
Compliance/audit |
~365 days |
45 days |
This flexibility allowed lifecycle management to reflect operational reality rather than arbitrary platform rules.
Quarantine became the most important stage of the entire workflow.
ACTIVE → FLAGGED → QUARANTINED → BACKED_UP → DELETED
↑ ↓
└── RESTORED (owner intervention)
Once content was identified as stale, it was moved into a controlled quarantine environment.
At that stage:
Importantly, the content itself remained intact.
This created a validation period.
Stakeholders could review the content and determine whether it was still required.
If restoration was needed, the process could be initiated before any destructive actions occurred.
Quarantine transformed cleanup from an irreversible decision into a reversible workflow.
A common mistake in automation projects is focusing exclusively on successful execution.
We chose to focus on recoverability instead.
Before any content entered the deletion phase, it was backed up.
def validate_backup(asset_id, backup_path):
checksum_original = compute_checksum(asset_id)
checksum_backup = compute_checksum(backup_path)
return checksum_original == checksum_backup
The backup process supported:
Deletion only became possible after backup validation succeeded.
This ensured that every removal remained reversible.
Recoverability wasn't treated as an optional feature.
It was a prerequisite.
Another challenge involved ownership. Over time, people leave organizations. Teams get reorganized. Projects change hands. As a result, some content no longer has a clearly identifiable owner.
This creates a serious problem for lifecycle management. Who receives notifications? Who approves restoration? Who determines whether content remains valuable?
To address this, we introduced an ownership reassignment process. Before quarantine, orphaned content was reassigned to team-level ownership structures.
def resolve_owner(asset):
if asset.owner.is_active:
return asset.owner
return get_team_owner(asset.project_id) or "unclaimed-review-queue"
This gave active stakeholders an opportunity to claim responsibility before any lifecycle actions occurred.
Lifecycle management without auditability quickly becomes problematic.
Every action taken by the platform needed to be traceable.
Every decision needed to be explainable.
The audit layer captured:
{
"asset_id": "Dashboard-0192",
"state": "QUARANTINED",
"previous_owner": "team:analytics-core",
"last_access_days_ago": 118,
"staleness_score": 0.81,
"backup_status": "validated",
"quarantine_date": "2026-04-02",
"restoration_requested": false,
"automation_run_id": "run-20260402-0007"
}
This allowed us to reconstruct the complete lifecycle of every managed asset.
More importantly, it enabled us to answer difficult questions months later.
Why was something removed?
Who owned it?
When was it last accessed?
Was it backed up?
Without auditability, those questions become difficult.
With auditability, they become routine.
One of the most interesting lessons from this project was that content lifecycle management is often misunderstood.
Most organizations view it as an operational activity.
In reality, it behaves much more like a platform engineering problem.
The objective is not simply deleting unused assets.
The objective is designing systems that can safely remove assets without creating risk.
That requires:
Interestingly, these same principles appear across many other engineering domains:
The underlying challenge remains remarkably consistent.
How do you automate removal without introducing unacceptable risk?
The most valuable lesson from this project extends beyond analytics platforms.
Most automation systems are designed primarily for successful execution.
The downside of that approach is that failure scenarios often receive less attention.
We deliberately reversed that thinking.
The platform was designed around safe failure.
What happens if content is misclassified?
What happens if ownership data is incorrect?
What happens if restoration is required?
By designing for those scenarios first, we were able to automate a process that would otherwise have remained manual.
Designing a content lifecycle management platform taught us that automation is not fundamentally about removing manual work. It's about creating systems that remain safe even when they make mistakes.
By introducing quarantine, recoverability, ownership management, and auditability into the cleanup process, we transformed a risky deletion problem into a predictable engineering workflow.
The result was not simply a cleaner platform. It was a more resilient one. And in large-scale platform engineering, resilience is often more valuable than efficiency.
Author’s note: This article reflects my personal views and is not written on behalf of, endorsed by, or intended to represent the views of my employer. It is intended for general informational purposes and does not constitute legal, technical, or other professional advice.