Context
When refactoring APIs or implementing features incrementally, you often have code that's temporarily unused but serves a legitimate purpose (backward compatibility, future functionality, tested APIs). Blindly deleting "dead code" can destroy working tests, remove intentional design elements, or eliminate useful utilities.
Applies to:
- •API migrations where old methods must coexist with new ones
- •Progressive feature implementation (theme colors defined before rendering)
- •Public API methods with test coverage but no production usage yet
- •Test utility methods that only exist for validation
Patterns
1. Use #[allow(dead_code)] for Intentional Reservations
When code has a valid reason to exist despite compiler warnings:
/// Check if a position is within the viewport
/// Reserved for future visibility culling optimization
#[allow(dead_code)]
pub fn is_visible(&self, pos: (u32, u32)) -> bool {
// Implementation...
}
When to use:
- •Public API methods with test coverage but no callers yet
- •Utility methods used only in tests
- •Struct fields that are part of a coherent design (theming, configuration)
- •Deprecated methods supporting backward-compatible tests
2. Mark Deprecated Methods Clearly
When phasing out an old API but keeping tests:
/// Legacy batch generation - deprecated in favor of progressive generation
#[deprecated(note = "Use generate_entrance() for progressive generation")]
#[allow(dead_code)]
pub fn generate(&mut self) -> Vec<Room> {
// Old implementation...
}
Benefits:
- •Tests continue to validate core logic (2D6 rules, exit placement)
- •Deprecation warning guides new code to use modern API
- •
#[allow(dead_code)]suppresses compiler noise for intentional backward compatibility
3. Document Rationale in Comments
Always explain WHY code is marked #[allow(dead_code)]:
// ✅ Good: Explains purpose /// Corridor color - reserved for future corridor rendering #[allow(dead_code)] pub corridor: Color, // ❌ Bad: No context #[allow(dead_code)] pub corridor: Color,
4. Delete Code That Truly Has No Purpose
If code has no tests, no callers, no deprecation marker, and no design purpose → delete it:
// ❌ Delete this:
pub fn unused_helper(&self) -> u32 {
// No tests, no callers, no documentation
42
}
5. Keep Helper Structs for Deprecated Methods
If a deprecated method needs supporting types, mark them too:
/// Helper for deprecated batch generation - tracks unexplored exits
#[derive(Debug)]
#[allow(dead_code)]
struct AvailableExit {
room_id: usize,
wall: Wall,
exit_index: usize,
}
#[deprecated(note = "Use generate_entrance() for progressive generation")]
#[allow(dead_code)]
pub fn generate(&mut self) -> Vec<Room> {
let exits: Vec<AvailableExit> = self.collect_exits();
// ...
}
Examples
Rust: Preserving Test Utilities
impl Room {
/// Calculate room area - used by tests and generation logic
#[allow(dead_code)]
pub fn area(&self) -> u32 {
self.width * self.height
}
/// Check if room is a corridor (1-width dimension)
#[allow(dead_code)]
pub fn is_corridor(&self) -> bool {
self.width == 1 || self.height == 1
}
}
// In tests:
#[test]
fn test_small_room_detection() {
let room = create_room(2, 3);
assert_eq!(room.area(), 6);
assert!(!room.is_corridor());
}
Rust: Theming System with Incomplete Rendering
pub struct Theme {
pub wall: Color,
pub floor: Color,
/// Corridor color - reserved for future corridor rendering
#[allow(dead_code)]
pub corridor: Color,
pub door: Color,
pub explorer: Color,
}
// Later when corridor rendering is added:
// 1. Remove #[allow(dead_code)]
// 2. Use theme.corridor in renderer
// 3. No API changes needed — field was always there
C#: Deprecated API Pattern
public class DungeonGenerator
{
[Obsolete("Use GenerateEntrance() for progressive generation")]
#pragma warning disable IDE0051 // Remove unused private members
public List<Room> Generate()
{
// Old batch generation logic
// Still used by 56 backward-compatibility tests
}
#pragma warning restore IDE0051
public Room GenerateEntrance()
{
// New progressive API
}
}
TypeScript: Future-Reserved Public API
export class Camera {
/**
* Check if a position is within the viewport
* Reserved for future visibility culling optimization
* @internal - has test coverage but not used in production yet
*/
// @ts-ignore - unused for now
isVisible(pos: [number, number]): boolean {
const [x, y] = pos;
return x >= this.x && x < this.x + this.width
&& y >= this.y && y < this.y + this.height;
}
}
// In tests:
test('isVisible detects out-of-bounds', () => {
const camera = new Camera(80, 24);
expect(camera.isVisible([100, 100])).toBe(false);
});
Anti-Patterns
❌ Don't: Suppress warnings without justification
#[allow(dead_code)] // Why? No one knows.
pub fn mystery_function() {}
✅ Do: Document the reason
/// Reserved for Phase 4 pathfinding optimizations
#[allow(dead_code)]
pub fn mystery_function() {}
❌ Don't: Delete tested public API methods just because production code doesn't call them
// ❌ Bad: This method has 5 tests validating boundary conditions!
// pub fn is_visible(&self, pos: (u32, u32)) -> bool { ... }
✅ Do: Preserve with #[allow(dead_code)] and note it's tested
/// Check if position is in viewport - reserved for visibility culling
#[allow(dead_code)]
pub fn is_visible(&self, pos: (u32, u32)) -> bool { ... }
❌ Don't: Keep deprecated code without deprecation markers
// ❌ Bad: No signal to developers that this is old
#[allow(dead_code)]
pub fn generate(&mut self) -> Vec<Room> { ... }
✅ Do: Mark as deprecated if keeping for backward compatibility
#[deprecated(note = "Use generate_entrance()")]
#[allow(dead_code)]
pub fn generate(&mut self) -> Vec<Room> { ... }
❌ Don't: Keep private helper functions for deleted features
// ❌ Bad: Parent function deleted, helper orphaned
#[allow(dead_code)]
fn collect_exits(&self) -> Vec<Exit> { ... }
✅ Do: Delete helpers when their only caller is removed
// Deleted collect_exits() when the only function using it was removed
Decision Framework
When facing a dead code warning, ask:
- •Is it tested? → Keep with
#[allow(dead_code)]+ comment - •Is it public API? → Keep with
#[allow(dead_code)]+ comment - •Is it deprecated but supporting tests? → Keep with
#[deprecated]+#[allow(dead_code)] - •Is it part of incomplete feature? (e.g., theme field not rendered yet) → Keep with
#[allow(dead_code)]+ comment - •None of the above? → Delete it
Benefits
- •Preserve test coverage without spurious warnings
- •Signal intent to future developers (this is here for a reason)
- •Smooth migrations from old APIs to new ones
- •Clean builds (zero warnings) without losing intentional code
- •Iterative development (define theme fields before using them)