Skip to content

What is the Deletable Modules Test? A Practical Guide to Writing Maintainable Code

What is the Deletable Modules Test? A Practical Guide to Writing Maintainable Code

I was working on a project last week when I noticed something strange. My “user notifications” feature was becoming impossible to manage. It had grown into a tangled mess of dependencies - database connections, global registries, and hardcoded references spread across 20+ files. When the product owner asked if we could disable notifications temporarily for performance testing, I realized I couldn’t do it without risking breaking half the application.

That’s when I discovered the deletable modules test. It’s simple: ask yourself one question before writing any code: “If a product owner told me this feature is no longer needed, how easy would it be to delete this module?

The Problem with Non-Deletable Code

Let me show you what I was dealing with:

user-notifications/index.js
import { EmailService } from './services/email-service'
import { SmsService } from './services/sms-service'
import { PushService } from './services/push-service'
import { logger } from '../shared/utils/logger'
import { db } from '../database/connection'
// Global registry mixed with business logic
const notificationRegistry = new Map()
function initNotifications() {
// Hard dependencies on global state
emailService = new EmailService(config)
smsService = new SmsService(config)
pushService = new PushService(config)
// Mixed concerns - registry logic mixed with initialization
notificationRegistry.set('email', emailService)
notificationRegistry.set('sms', smsService)
// Side effects - modifies global state
db.on('user_created', handleUserCreated)
logger.info('Notification system initialized')
}
function handleUserCreated(user) {
// Tight coupling with other systems
const notifications = notificationRegistry.values()
for (const service of notifications) {
service.notify(user)
}
}

I got error after error when trying to disable this feature. The module wasn’t just “disabled” - it was so tightly woven into the application that removing it felt like performing open-heart surgery with a butter knife.

What Makes a Module Deletable?

Here’s the difference - this is how I rewrote the notifications module:

user-notifications/services/email-service.js
export class EmailService {
constructor(config) {
this.transporter = config.transporter
}
async sendWelcomeEmail(user) {
await this.transporter.sendMail({
to: user.email,
subject: 'Welcome!',
html: this.buildWelcomeTemplate(user)
})
}
buildWelcomeTemplate(user) {
return `Welcome ${user.name}!`
}
}

This deletable module:

  • Has no external dependencies beyond configuration
  • Doesn’t modify global state
  • Can be removed by deleting a single file
  • Doesn’t have side effects
  • Is testable in isolation

The Trial and Error Process

I tried disabling notifications in my first attempt by just commenting out the initNotifications() call. Result? Three services broke because they still expected notifications to work.

Error: Cannot read property 'sendMail' of undefined
Error: Notification service not found
Error: Event listener not found

My second attempt was to try to “disable” individual services within the module. That worked… until a new feature somewhere in the code started expecting all services to be available.

Finally, I understood the problem: the module wasn’t deletable because it violated the single responsibility principle and had hidden dependencies.

How This Test Prevents Overengineering

The deletable modules test is like a mental boundary guard. When I started using it, I noticed three immediate improvements:

1. Forced Clear Boundaries

If I can’t imagine deleting a module without breaking other parts, it probably has unclear responsibilities. I learned to ask: “What exactly is this module responsible for, and nothing else?

2. Revealed Hidden Dependencies

My notification module had a hidden dependency on the database connection that wasn’t obvious from the surface. The test made me question every import and dependency.

3. Prevented Feature Creep

Before, I’d add “just one more feature” to a module because it was convenient. Now I ask: “Would this make the module harder to delete?” Most times, the answer is yes.

Connection to SOLID Principles

This test isn’t just about deletion - it’s about good architecture:

  • Single Responsibility: Deletable modules naturally follow SRP
  • Open/Closed: They’re open for extension but closed for modification
  • Liskov Substitution: Clear interfaces make swapping easy
  • Interface Segregation: Small modules have focused interfaces
  • Dependency Inversion: They depend on abstractions, not concrete implementations

My Implementation Checklist

Before creating any new module, I ask these questions:

  1. Can I delete this module with a single file deletion?
  2. Does it depend on specific implementations rather than abstractions?
  3. Does it modify global state or external systems?
  4. Is it easily testable in isolation?

For existing code, I count:

  • Files that would need modification to remove this feature
  • Global state dependencies
  • Circular dependencies
  • Hardcoded references

Real-World Results

After refactoring my notifications module, I was able to:

  • Disable notifications for performance testing in under 5 minutes
  • Replace email notifications with push-only notifications in 1 hour
  • Remove the entire notifications feature when we switched to a third-party service

The codebase became more maintainable overnight because each module had clear boundaries and responsibilities.

Why This Test Works

The beauty of the deletable modules test is its simplicity. Unlike complex architectural patterns, it’s a quick mental check that forces better decisions without overthinking. When you write deletable code, you naturally:

  • Create focused, single-purpose modules
  • Use dependency injection over global state
  • Implement clear interfaces
  • Avoid circular dependencies
  • Keep code DRY (Don’t Repeat Yourself) without over-consolidating

Conclusion

The deletable modules test is my secret weapon against technical debt. It’s not about actually deleting code - it’s about ensuring every module could be deleted if needed. This simple question pushes me toward better architecture every day.

Try it on your next feature: before writing code, ask “how easy would this be to delete?” You’ll be surprised at how it improves your design decisions.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments