ICT:D Cleaning up Unused Custom Modules (Composer - Drush)
Drupal: Cleaning Up Unused Custom Modules (Composer + Drush)
Purpose
This page documents the **correct and safe procedure** for removing unused or experimental **custom Drupal modules**, including:
- database cleanup
- configuration cleanup
- file system cleanup
The procedure avoids common pitfalls and is suitable for long‑lived Drupal installations that must remain upgrade‑safe.
---
Important Principles
Drupal deliberately separates:
| Aspect | Managed by |
|---|---|
| Module enable/disable | Drupal |
| Configuration & schema cleanup | Module uninstall logic |
| Module files on disk | You / Composer |
Drupal never deletes module files automatically. This is a security and deployment design choice.
---
Never Do This
- ❌ Do NOT delete module files before uninstalling
- ❌ Do NOT drop tables while the module is still enabled
- ❌ Do NOT rely on Drupal to clean up experimental schema automatically
Doing so may leave broken configuration or database artifacts.
---
Correct Cleanup Procedure
Step 1: Verify the Module Is No Longer Used
Before removal, ensure:
- no content depends on the module
- no fields, entities, or views reference it
- no other module lists it as a dependency
---
Step 2: Uninstall the Module (Mandatory)
Uninstalling runs:
hook_uninstall()- configuration cleanup
- entity cleanup (if implemented correctly)
Using Drush (recommended)
drush pmu my_custom_module
Using the Admin UI
- Go to
Extend → Uninstall - Select the module
- Confirm uninstall
Note: Disabling a module is NOT sufficient. You must uninstall it.
---
Step 3: Remove the Module Files
Drupal will NOT remove files from disk.
Custom module (manual)
rm -rf modules/custom/my_custom_module
Contrib module (Composer-managed)
composer remove drupal/my_module
This removes:
- the module directory
- composer.json / composer.lock references
---
Step 4: Clear Caches
After file removal:
drush cr
This ensures Drupal no longer tries to discover the module.
---
Database Cleanup (If Needed)
Custom Tables
Drupal does NOT drop custom tables automatically unless explicitly coded.
If the module created a “real” join table or custom schema:
DROP TABLE my_custom_join_table;
Only do this:
- after uninstalling the module
- when you are certain the table is unused
This is normal for experimental schema.
---
How to Check for Leftovers
Search the database for the module name:
grep my_custom_module database_dump.sql
Or inspect:
configkey_valuecache_*tables (usually cleared by cache rebuild)
---
Common Error Scenario
If files are deleted before uninstalling:
- Drupal reports:
“The following module is missing from the file system…”
- Uninstall hooks never ran
- Manual DB cleanup is required
This is why uninstall must always come first.
---
Why Drupal Works This Way
Drupal avoids automatic file deletion because:
- web servers should not delete their own code
- production file systems are often read‑only
- deployments are controlled externally
- safety is preferred over convenience
This design supports secure, professional deployments.
---
Architectural Lesson
Custom modules:
- create lifecycle responsibility
- require manual cleanup
- increase long‑term maintenance cost
Configuration‑driven solutions:
- leave no schema debris
- uninstall cleanly
- survive core upgrades
This cleanup step is not housekeeping — it is architectural alignment.
---
Summary Checklist
| Step | Required |
|---|---|
| Uninstall module (pmu) | ✅ Yes |
| Remove module files | ✅ Yes |
| Clear caches | ✅ Yes |
| Drop custom tables (if any) | ✅ Manual |
| Verify no leftovers | ✅ Recommended |
---
Final Note
Experimental work is valuable.
Cleaning it up properly:
- preserves system integrity
- simplifies upgrades
- helps your successor
- reflects professional discipline