Skip to content

refactor: extract shared markdown builders for rules generation strat…#74

Closed
mkczarkowski wants to merge 2 commits intomasterfrom
refactor-markdown-builders
Closed

refactor: extract shared markdown builders for rules generation strat…#74
mkczarkowski wants to merge 2 commits intomasterfrom
refactor-markdown-builders

Conversation

@mkczarkowski
Copy link
Collaborator

@mkczarkowski mkczarkowski commented Sep 29, 2025

Problem

The SingleFileRulesStrategy and MultiFileRulesStrategy classes contained significant code duplication for generating markdown content. Both strategies independently implemented:

  • Project header creation
  • Empty state messaging
  • Library content formatting
  • Section structure rendering

This duplication made the codebase harder to maintain and increased the risk of inconsistencies when making changes.

Solution

Extracted common markdown generation logic into a shared utility module rulesMarkdownBuilders.ts containing:

Shared Functions

  • createProjectMarkdown() - Generates consistent project headers
  • createEmptyStateMarkdown() - Creates empty state messages
  • generateLibraryContent() - Formats individual library rules
  • renderLibrarySection() - Renders complete library sections with headers
  • generateLibrarySections() - Builds all library sections organized by layer/stack
  • PROJECT_FILE_CONFIG - Centralizes default file configuration

Changes Made

  • Refactored SingleFileRulesStrategy to use shared utilities (removed ~40 lines)
  • Refactored MultiFileRulesStrategy to use shared utilities (removed ~20 lines)
  • Created new shared module with well-documented, reusable functions

Benefits

Eliminates ~60 lines of duplicate code
Ensures consistent formatting across all strategies
Simplifies adding new strategies - just import and use shared functions
Makes copy changes easier - update in one place, affects all strategies
Improves maintainability - single source of truth for markdown generation
Better testability - shared functions can be unit tested independently

Testing

All existing tests pass without modification, confirming this is a pure refactoring with no behavior changes.

npm run test ✅
npm run lint:check ✅

🤖 Generated with Claude Code

…egies

Extract common scaffolding from Single and Multi file strategies into reusable builders module. This reduces code duplication and ensures consistent formatting across both strategies.

Key changes:
- Created markdown-builders module with shared utilities
- Extracted project header, empty state, and library section rendering
- Unified iteration logic over layer/stack/library structure
- Simplified both strategies to use shared builders

Benefits:
- Guarantees consistent formatting across strategies
- Reduces effort when adding new strategies
- Single source of truth for markdown generation logic
- Easier maintenance and testing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link

✅ All checks have passed successfully!

  • Lint: ✅
  • Unit Tests: ✅
  • E2E Tests: ✅

Coverage reports have been uploaded as artifacts.

@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @mkczarkowski's task —— View job


Code Review Complete ✅

Tasks

  • Analyze PR changes and diff against origin/master
  • Read context documents (project PRD, tech stack)
  • Review new shared utilities module
  • Review refactored strategy classes
  • Check for code quality and best practices
  • Evaluate for potential bugs or regressions
  • Assess security and performance implications
  • Provide comprehensive feedback

Summary

This PR successfully extracts common markdown generation logic from SingleFileRulesStrategy and MultiFileRulesStrategy into a shared utilities module. The refactoring achieves its stated goal of reducing code duplication (~60 lines removed) and establishing a single source of truth for markdown formatting.

Overall Assessment: Approve with Minor Suggestions

The refactoring is well-executed and all CI checks pass. However, I've identified several areas for improvement regarding type safety, unused code, and clarity.


Detailed Review

✅ Strengths

  1. Excellent DRY Principle Application: Successfully eliminates significant duplication between both strategy classes
  2. Clean Abstractions: The extracted functions have clear, single responsibilities
  3. Backward Compatible: All existing tests pass without modification, confirming no behavioral changes
  4. Good Documentation: Functions are well-named and the PR description is comprehensive
  5. Consistent Formatting: Centralizes markdown generation logic in one place

⚠️ Issues & Suggestions

1. Type Safety Issues (Medium Priority)

Location: src/services/rules-builder/markdown-builders/index.ts:16-22, index.ts:99

The LibrarySectionConfig interface and createLibraryFileMetadata function use string types instead of the more specific Layer, Stack, and Library enum types:

// Current (less type-safe)
export interface LibrarySectionConfig {
  layer: string;
  stack: string;
  library: string;
  // ...
}

// Recommended (more type-safe)
export interface LibrarySectionConfig {
  layer: Layer;
  stack: Stack;
  library: Library;
  includeLayerHeader?: boolean;
  includeStackHeader?: boolean;
}

Impact: This loses compile-time type checking and IDE autocomplete benefits. The Layer, Stack, and Library enums from dictionaries.ts should be used throughout for stronger type guarantees.

Recommendation: Update all parameter types to use the enum types instead of string.


2. Unused Function (Low Priority)

Location: src/services/rules-builder/markdown-builders/index.ts:57-62

The renderLibraryRulesContent function is exported but doesn't appear to be used by either strategy class:

export const renderLibraryRulesContent = (library: Library): string => {
  const libraryRules = getRulesForLibrary(library);
  return libraryRules.length > 0
    ? libraryRules.map((rule) => `- ${rule}`).join('\n')
    : `- Use ${library} according to best practices`;
};

Recommendation:

  • If this is for future use, add a JSDoc comment explaining its purpose
  • If it's truly unused, remove it to keep the codebase clean
  • If it's used elsewhere in the codebase, ignore this suggestion

3. Unused Callback Parameters (Low Priority)

Location: src/services/rules-builder/markdown-builders/index.ts:64-74

The iterateLayersStacksLibraries function calculates isFirstInStack and isFirstInLayer but neither strategy actually uses these parameters:

onLibrary: (
  layer: Layer,
  stack: Stack,
  library: Library,
  isFirstInStack: boolean,  // ← Not used
  isFirstInLayer: boolean,   // ← Not used
) => void;

Recommendation:

  • If these are for future extensibility, add JSDoc documentation explaining the intended use case
  • If they're not needed, simplify by removing them
  • Consider whether the complexity added by tracking these booleans is worth it

4. Unclear Spacing Logic (Low Priority)

Location: src/services/rules-builder/rules-generation-strategies/SingleFileRulesStrategy.ts:57-60

This conditional logic for adding newlines is unclear:

if (includeStackHeader) {
  currentStack = stack;
  if (!includeLayerHeader && currentStack) {
    markdown += '\n';
  }
}

Issue: The condition !includeLayerHeader && currentStack is checking if currentStack is truthy, but it was just assigned, so this is essentially checking if stack is truthy. The intent isn't clear.

Questions:

  • What happens when currentStack is initially an empty string?
  • Is the goal to add spacing between stacks but not after layers?

Recommendation: Add a comment explaining the exact spacing behavior, or refactor for clarity:

// Add spacing between stack sections (but not after a new layer header)
if (includeStackHeader && !includeLayerHeader && currentStack !== '') {
  markdown += '\n';
}

5. Type Narrowing Opportunity (Code Quality)

Location: src/services/rules-builder/rules-generation-strategies/MultiFileRulesStrategy.ts:47-55

The destructured parameters could be more explicitly typed:

// Current
private buildRulesContent({
  layer,
  stack,
  library,
}: {
  layer: string;
  stack: string;
  library: string;
}): RulesContent {

Recommendation: Use the specific enum types for better clarity:

private buildRulesContent({
  layer,
  stack,
  library,
}: {
  layer: Layer;
  stack: Stack;
  library: Library;
}): RulesContent {

🔒 Security Assessment

No security concerns identified

  • No user input handling in this code
  • No external API calls or data persistence
  • Pure string manipulation with static data
  • No sensitive data exposure

⚡ Performance Assessment

No performance concerns

  • String concatenation is efficient for the expected scale
  • No unnecessary iterations or N+2 queries
  • The refactoring doesn't change algorithmic complexity
  • Memory footprint remains the same

🧪 Testing Recommendations

While the existing tests pass (confirming no regressions), consider adding:

  1. Unit tests for the new utilities module (markdown-builders/index.ts)

    • Test createProjectMarkdown() with various inputs
    • Test renderLibrarySection() with different header combinations
    • Test iterateLayersStacksLibraries() with edge cases (empty structures, single items)
  2. Integration tests to verify consistent output between strategies

    • Ensure both strategies produce identically formatted markdown for the same library
    • Test edge cases like empty library lists, missing rules, etc.

These tests would make the shared utilities more robust and easier to modify in the future.


📝 Additional Observations

  1. Good naming conventions: Function names clearly describe their purpose
  2. Consistent code style: Follows TypeScript and project conventions
  3. No breaking changes: The refactoring is internal and doesn't affect the public API
  4. Future-friendly: The strategy pattern makes adding new output strategies straightforward

Conclusion

This is a solid refactoring that achieves its goals effectively. The identified issues are minor and mostly relate to type safety and code clarity rather than functional problems. The PR demonstrates good software engineering practices:

  • ✅ Proper use of DRY principle
  • ✅ Clear separation of concerns
  • ✅ Backward compatibility maintained
  • ✅ All CI checks passing

Recommended Actions (in priority order):

  1. Fix type safety issues by using enum types instead of strings
  2. Remove or document the unused renderLibraryRulesContent function
  3. Clarify the spacing logic in SingleFileRulesStrategy
  4. Consider removing unused callback parameters or documenting their future use
  5. Add unit tests for the new shared utilities

Note: This PR currently shows 0 files changed because the refactor-markdown-builders branch was merged with master after the refactoring commit (3903d42). The actual changes are in commit 3903d42, which adds 102 lines in the new markdown-builders module and refactors both strategy classes.

--- | Branch: refactor-markdown-builders

@github-actions
Copy link

✅ All checks have passed successfully!

  • Lint: ✅
  • Unit Tests: ✅
  • E2E Tests: ✅

Coverage reports have been uploaded as artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants