websocket-chat / CODE_REVIEW_REPORT.md
harsh-dev's picture
Add application file
a7476ec unverified

Code Review & Refactoring Report

Overview

This report analyzes the current codebase (sockets project) and provides actionable feedback to improve maintainability, performance, and structure. The project uses Express and ws for a simple WebSocket server with room management.


1. Architectural Improvements

Issue: specialized "Service" instantiated per message

Currently, a new MessageService instance is created for every single incoming message.

// src/index.ts
new MessageService({ msg: msg.toString(), ... })

This is inefficient and semantically incorrect for a "Service". A Service should ideally be a singleton or a static utility that handles logic, rather than an ephemeral object representing a single request.

Recommendation: Refactor MessageService to contain static methods or be a singleton instance that accepts the message and socket as arguments to a handleMessage method.

Issue: Tight Coupling and Logic in Constructor

Critical business logic (parsing, routing, executing side effects) happens inside the constructor of MessageService.

// src/services/MessageService.ts
constructor(...) {
    // ... logic ...
    if(action === 'join') ...
}

Constructors should primarily initialize state. Side effects (like broadcasting messages) should happen in explicit methods.

Recommendation: Move logic out of the constructor. Create a handleIncomingMessage(data: string, socket: WebSocket) method.


2. Robustness and Error Handling

Issue: Unsafe JSON.parse

If a client sends an invalid JSON string, JSON.parse(msg) inside MessageService will throw an error. Since this is not wrapped in a try-catch block, it could crash the application or unexpectedly terminate the socket connection.

Recommendation: Wrap the parsing logic in a try-catch block. If parsing fails, send an error response to the sender and abort processing.

Issue: Missing Input Validation

The code assumes room_id, username, and action always exist in the parsed payload.

this.room_id = layers['room_id'] // Could be undefined

Accessing properties on undefined or unexpected data types can lead to runtime errors.

Recommendation: Use a schema validation library (like Zod) or manual checks to ensure incoming data matches the expected structure (Interface) before processing.


3. Type Safety and TypeScript Best Practices

Issue: any types implicitly

JSON.parse returns any. You should define an interface for your message structure.

interface WebSocketMessage {
    room_id: string
    username: string
    message: string
    action: 'join' | 'leave' | 'message'
}

Issue: Encapsulation

In RoomServices, the map property is public.

map: Map<string, Map<string, WebSocket>>

This allows external code to modify the state directly, bypassing helper methods like leaveRoom.

Recommendation: Make map private: private map: Map<...>.


4. Code Cleanliness & Manageability

Magic Strings

Strings like 'join' and 'leave' are hardcoded in multiple places. Recommendation: use an enum or constants for Action types.

enum SocketAction {
    JOIN = 'join',
    LEAVE = 'leave',
    MESSAGE = 'message',
}

Duplicate Logic

In MessageService.broadcast, the check for room existence is repeated redundant calls.


Proposed Refactored Structure

src/types.ts

Define shared interfaces and enums here.

src/services/RoomManager.ts (Renamed from RoomServices)

Keep it focused on state management (add, remove, get users). Ensure data is private.

src/handlers/MessageHandler.ts (Renamed from MessageService)

A stateless function or class that:

  1. Parses the message (safely).
  2. Validates the schema.
  3. Calls the appropriate action on RoomServices.

src/index.ts

Initialize the App and pass the singletons/handlers to the WebSocket event listener.


Summary of Next Steps

  1. Safety: Add try-catch around JSON parsing.
  2. Organization: Separate "Data Types" from "Logic".
  3. Pattern: Stop new MessageService(...) pattern. Use MessageController.handle(...).
  4. Resilience: Validate all inputs before using them.