Code Quality Guidelines

Comprehensive Software Development Guidelines

Table of Contents


General Coding Guidelines

Naming and Organization

  1. Naming Conventions
  • Use descriptive, intention-revealing names for variables, methods, and classes
  • Avoid abbreviations unless universally understood
  • Follow language-specific conventions (camelCase, PascalCase, etc.)
  • Name boolean variables with prefixes like “is”, “has”, or “should”
  1. Code Organization
  • Separate static and dynamic parts of your application
  • Follow a consistent, logical file and directory structure
  • Group related functionality together
  • Keep source files focused on a single responsibility

Coding Practices

  1. No Hard Coding
  • Define constants or enums in appropriate locations
  • Use configuration files for environment-specific values
  • Centralize configuration management
  1. Simplicity and Complexity
  • Prefer simplicity over complexity
  • If code becomes complex, it’s likely a candidate for refactoring
  • Remember: “It’s hard to build simple things”
  • Complex solutions usually indicate design issues
  1. Optimization
  • Avoid premature optimization
  • Optimize only after profiling and identifying actual bottlenecks
  • Consider algorithmic efficiency (Big O notation) for critical paths
  1. Design Patterns
  • Look for opportunities to apply standard design patterns
  • Adapt patterns to fit your specific use case
  • Document pattern usage for clarity
  1. Code Repetition
  • Strictly prohibit repetitive code (DRY – Don’t Repeat Yourself)
  • Extract repeated logic into reusable methods or classes
  • Consider refactoring when similar code appears multiple times
  1. Code Formatting
  • Consistently align and indent code
  • Follow language/framework-specific style guides
  • Use automated formatting tools
  • Always format before committing code

Class Design

  1. Size and Responsibility
  • Keep classes under 600 lines (smaller is better)
  • Follow Single Responsibility Principle
  • Each class should have one clear purpose
  1. Constructors
  • Keep constructors simple and exception-safe
  • Use dependency injection for dependencies
  • Initialize essential state only
  1. Composition vs Inheritance
  • Prefer composition over inheritance
  • Use inheritance only for genuine “is-a” relationships
  • Avoid deep inheritance hierarchies
  1. Extensibility
  • Design classes for extensibility
  • Implement the Open/Closed Principle
  • Consider future use cases without overengineering

Functions and Methods

  1. Size and Complexity
  • Keep functions under 25 lines (ideally 5-10 lines)
  • Each function should do one thing and do it well
  • Extract complex operations into separate functions
  1. Parameters
  • Validate parameters in public methods
  • Throw exceptions for invalid inputs
  • Keep parameter count low (ideally 0-3)
  • Use parameter objects for multiple related parameters
  1. Return Values
  • Avoid returning null when possible
  • Return empty collections instead of null for collections
  • Consider using Optional for potentially missing values
  • Try to avoid multiple return statements
  1. Organization
  • Group functions logically
  • Separate functions for initialization and business logic
  • Keep functions testable (avoid side effects)

Control Flow

  1. Conditional Statements
  • Avoid deep nested if/else statements (max 2-3 levels)
  • Use guard clauses for early returns
  • Break complex conditions into well-named helper methods or variables
  • Use parentheses in long conditions to clarify precedence
  1. Loops
  • Keep loop bodies simple
  • Extract complex loop bodies into separate functions
  • Consider functional approaches where appropriate
  • Be aware of performance implications for large datasets

Error Handling

  1. Exception Strategy
  • Define a clear exception hierarchy
  • Don’t suppress exceptions without handling them
  • Maintain the original exception’s cause when wrapping
  • Document exceptions in method contracts
  1. Exception Handling
  • Do not write complex code in exception handlers
  • Extract try/catch blocks to dedicated functions
  • Log sufficient information for troubleshooting
  • Follow language-specific best practices

Comments and Documentation

  1. Effective Comments
  • Write comments for complex or non-obvious code
  • Explain why, not what (the code shows what)
  • Keep comments up-to-date when code changes
  • Use standardized comment formats (JavaDoc, JSDoc, etc.)
  1. Error Messages
  • Create clear, actionable error messages
  • Use error message frameworks for consistency
  • Avoid exposing sensitive information in errors
  • Include relevant context for troubleshooting

Logging and Monitoring

  1. Logging Strategy
  • Use appropriate log levels (ERROR, WARN, INFO, DEBUG)
  • Log contextual information (request IDs, user IDs)
  • Follow consistent log patterns
  • Avoid logging sensitive information
  1. Performance Considerations
  • Be mindful of logging overhead
  • Use conditional logging for verbose information
  • Consider log aggregation and search requirements

Clean Code Principles

Code Clarity

  1. Intention-Revealing Names
  • Name variables, methods, and classes to reveal their purpose
  • Avoid abbreviations and acronyms unless universally understood
  • Use pronounceable names that can be discussed in conversation
  • Example: getUserTransactionHistory() instead of getUsrTrHist()
  1. Function Design
  • Functions should do one thing, do it well, and do it only
  • Functions should be small – ideally 5-10 lines
  • Reduce function parameters to absolute minimum (0-2 is ideal)
  • Functions that modify state should not return values (Command-Query Separation)
  • Extract try/catch blocks into their own functions for cleaner code
  1. Comments
  • Good code mostly documents itself; comments should explain “why” not “what”
  • Comments that merely echo the code are worse than no comments
  • Keep comments relevant and up-to-date or delete them
  • Use comments for legal information, explanation of intent, clarification, and warnings

Structure and Organization

  1. The Scout Rule
  • Always leave the code cleaner than you found it
  • Make incremental improvements with each touch
  • Refactor gradually to avoid breaking changes
  1. Step-Down Rule
  • Organize code to read like a top-down narrative
  • Each function should be followed by those at the next level of abstraction
  • Creates natural flow from high-level concepts to implementation details
  1. Cohesion and Coupling
  • Classes should be highly cohesive (focused on a single responsibility)
  • Minimize coupling between classes and packages
  • Law of Demeter: Only talk to your immediate friends (avoid chains like a.getB().getC().doSomething())

SOLID Principles

  1. Single Responsibility Principle (SRP)
  • A class should have only one reason to change
  • Separate business logic from infrastructure concerns
  • Example: Separate OrderProcessor from OrderRepository
  1. Open/Closed Principle (OCP)
  • Software entities should be open for extension but closed for modification
  • Use abstractions and polymorphism to allow behavior changes without modifying existing code
  • Example: Strategy pattern for payment processing methods
  1. Liskov Substitution Principle (LSP)
  • Subtypes must be substitutable for their base types without altering program correctness
  • Override methods should not violate parent class contracts
  • Example: Square is not a proper subtype of Rectangle if it violates Rectangle’s behavior
  1. Interface Segregation Principle (ISP)
  • Clients should not be forced to depend on methods they do not use
  • Create specific interfaces rather than general-purpose ones
  • Example: Split large interfaces into OrderCreator, OrderFinder, etc.
  1. Dependency Inversion Principle (DIP)
  • High-level modules should not depend on low-level modules; both should depend on abstractions
  • Abstractions should not depend on details; details should depend on abstractions
  • Example: Service depends on Repository interface, not implementation

Clean Architecture

  1. Independence from Frameworks
  • The framework is a tool, not the architecture
  • Core business logic should be isolated from framework code
  • Use adapters/wrappers to interact with frameworks
  1. Testability
  • Business rules should be testable without UI, database, or external services
  • Use dependency injection to allow substituting test doubles
  • Create a testing strategy for each architectural layer
  1. Independence of Delivery Mechanism
  • Business logic should not know or care whether it’s being accessed via web, console, or API
  • Separate domain models from data transfer objects (DTOs)
  • Use mappers to convert between domain and external representations
  1. Screaming Architecture
  • Your architecture should “scream” the intent of the system
  • Package structure should reflect business domains, not technical concerns
  • Example: com.company.billing, com.company.shipping instead of com.company.controllers, com.company.services

Layered Architecture

  1. Layer Separation
  • Implement strict layering (e.g., Controller → Service → Repository)
  • Each layer has specific responsibilities
  • Upper layers call lower layers, never vice versa
  1. Layer Responsibilities
  • Presentation Layer: Handles user interaction and display
  • Service Layer: Contains business logic and orchestrates operations
  • Data Access Layer: Manages data storage and retrieval
  • Domain Layer: Contains business entities and rules
  1. Package Structure
  • Organize by feature or domain, not technical layer
  • Follow consistent naming conventions
  • Place functions in domain-appropriate packages
  • Avoid generic “utils” packages when possible

Java-Specific Guidelines

Core Java Features

  1. Effective Use of Java Language Features
  • Prefer enhanced for loops over traditional loops
  • Use annotations for configuration instead of XML when possible
  • Use var judiciously for local variables to reduce verbosity (Java 10+)
  • Avoid checked exceptions for predictable cases; use runtime exceptions or Optional
  • Use @Override annotation for all overridden methods
  1. Enums
  • Use enums instead of constants for related groups of values
  • Leverage enum methods and fields for related functionality
  • Consider using enum-based singletons for type-safe factories
   public enum PaymentMethod {
       CREDIT_CARD(true) {
           @Override
           public void process(Payment payment) {
               // Process credit card payment
           }
       },
       PAYPAL(true) {
           @Override
           public void process(Payment payment) {
               // Process PayPal payment
           }
       },
       INVOICE(false) {
           @Override
           public void process(Payment payment) {
               // Process invoice payment
           }
       };

       private final boolean isOnline;

       PaymentMethod(boolean isOnline) {
           this.isOnline = isOnline;
       }

       public boolean isOnlineMethod() {
           return isOnline;
       }

       public abstract void process(Payment payment);

       public static List<PaymentMethod> getOnlineMethods() {
           return Arrays.stream(values())
                    .filter(PaymentMethod::isOnlineMethod)
                    .collect(Collectors.toList());
       }
   }
  1. Generics
  • Use generics for type safety and to avoid casts
  • Understand PECS: “Producer Extends, Consumer Super”
  • Use wildcards appropriately to increase API flexibility
   // Producer (read from) - use extends
   public void process(List<? extends Animal> animals) {
       for (Animal animal : animals) {
           // can read animals
       }
   }

   // Consumer (write to) - use super
   public void addCats(List<? super Cat> catList) {
       catList.add(new Cat()); // can add cats or its subtypes
   }
  1. Default Methods
  • Use default methods to evolve interfaces without breaking implementations
  • Keep default methods simple, avoiding complex state or dependencies
   public interface UserRepository {
       List<User> findByRole(Role role);

       // Default method to find admins
       default List<User> findAdmins() {
           return findByRole(Role.ADMIN);
       }
   }
  1. Records (Java 16+)
  • Use records for simple data carriers
  • Leverage compact constructors for validation
  • Combine with sealed classes for complete domain modeling
   public record OrderDetails(
       String orderId,
       BigDecimal amount,
       LocalDateTime createdAt
   ) {
       // Compact constructor for validation
       public OrderDetails {
           Objects.requireNonNull(orderId, "Order ID cannot be null");
           Objects.requireNonNull(amount, "Amount cannot be null");
           Objects.requireNonNull(createdAt, "Created date cannot be null");

           if (amount.compareTo(BigDecimal.ZERO) <= 0) {
               throw new IllegalArgumentException("Amount must be positive");
           }
       }
   }
  1. Streams and Lambda Expressions
  • Use streams for collection processing to improve readability
  • Avoid side effects in stream operations
  • Use method references instead of lambdas when possible
  • Know when to use parallel streams (CPU-intensive operations on large collections)
  • Extract complex stream operations into well-named methods
   // Good: Clear, functional style with descriptive method references
   public List<UserDto> getActiveUserDtos() {
       return userRepository.findAll().stream()
           .filter(User::isActive)
           .filter(this::hasPermissions)
           .map(userMapper::toDto)
           .collect(Collectors.toList());
   }

   // Avoid: Overly complex stream pipeline
   public List<UserDto> getActiveUserDtos() {
       return userRepository.findAll().stream()
           .filter(user -> user.getStatus() == Status.ACTIVE 
               && !user.getRoles().isEmpty() 
               && user.getLastLogin().isAfter(LocalDate.now().minusDays(30)))
           .map(user -> new UserDto(user.getId(), user.getName(), user.getEmail()))
           .collect(Collectors.toList());
   }
  1. Optional Usage
  • Use Optional as a return type, not a parameter type
  • Avoid creating Optional objects for null checks
  • Leverage Optional methods like map(), filter(), and orElse()
  • Don’t use get() without checking isPresent() first; prefer orElse*() methods
   // Good: Fluent Optional usage
   public String getUserDisplayName(Long userId) {
       return userRepository.findById(userId)
           .map(User::getDisplayName)
           .orElse("Guest");
   }

   // Avoid: Improper Optional usage
   public Optional<User> findUser(Optional<String> username) { // Don't use Optional as parameter
       if (username.isPresent()) {
           return Optional.ofNullable(userRepository.findByUsername(username.get()));
       }
       return Optional.empty();
   }

Collections & Data Structures

  1. Collection Selection
  • Choose the appropriate collection for your use case:
    • ArrayList: Fast random access, slower insertions/deletions
    • LinkedList: Fast insertions/deletions, slower random access
    • HashSet: Fast lookups, no ordering guarantees
    • TreeSet: Sorted elements, slower than HashSet
    • HashMap: Fast key lookups
    • LinkedHashMap: Predictable iteration order
    • TreeMap: Sorted keys
  1. Collection Factory Methods (Java 9+)
  • Use collection factory methods for creating small, immutable collections
   // Good: Concise immutable collections
   List<String> colors = List.of("red", "green", "blue");
   Set<String> primaryColors = Set.of("red", "blue", "yellow");
   Map<String, Integer> ratings = Map.of(
       "Star Wars", 5,
       "Inception", 4,
       "Titanic", 3
   );
  1. Collection Performance
  • Set initial capacity when you know the approximate size
  • Use EnumMap and EnumSet for enum-based keys for better performance
  • Use ArrayDeque instead of Stack for stack operations
   // Good: Improved performance with proper sizing and specialized collections
   Map<String, User> userMap = new HashMap<>(1000); // Preallocate for 1000 users

   // Use EnumMap for enum keys
   Map<UserRole, List<User>> usersByRole = new EnumMap<>(UserRole.class);

   // Use ArrayDeque instead of Stack
   Deque<Command> commandStack = new ArrayDeque<>();
   commandStack.push(command);
   Command lastCommand = commandStack.pop();
  1. Type Safety
  • Always use generics when working with collections
  • Declare collection variables with interface types:
   // Good:
   Map<String, User> users = new HashMap<>();
   List<Order> orders = new ArrayList<>();
  • Avoid raw types
  • Avoid using Object as a parameter or return type

Date and Time

  1. Modern Date/Time API
  • Use java.time package instead of legacy Date/Calendar
  • Choose appropriate classes for your needs:
    • LocalDate for date without time
    • LocalTime for time without date
    • LocalDateTime for date and time without timezone
    • ZonedDateTime for date and time with timezone
    • Instant for machine timestamps
  1. Timezone Handling
  • Always specify timezones explicitly when needed
  • Store dates in UTC internally
  • Convert to user’s timezone only for display
  • Use a consistent timezone throughout your application
  1. Formatting and Parsing
  • Use DateTimeFormatter for formatting and parsing
  • Create reusable formatters as constants
  • Consider locale when formatting dates for display
   // Define reusable formatters
   private static final DateTimeFormatter ISO_FORMATTER = 
       DateTimeFormatter.ISO_DATE_TIME;

   private static final DateTimeFormatter DISPLAY_FORMATTER = 
       DateTimeFormatter.ofPattern("MMM d, yyyy h:mm a", Locale.US);

   // Parsing
   LocalDateTime dateTime = LocalDateTime.parse(isoString, ISO_FORMATTER);

   // Formatting
   String displayDate = dateTime.format(DISPLAY_FORMATTER);

String Handling

  1. Efficient String Operations
  • Use StringBuilder for string concatenation in loops
  • Use String.format() or text blocks (Java 15+) for multi-line strings
  • Be careful with string splitting and regular expressions in performance-critical code
  1. Text Blocks (Java 15+)
  • Use text blocks for SQL queries, HTML, JSON, etc.
   String sql = """
       SELECT u.id, u.name, u.email
       FROM users u
       JOIN roles r ON u.role_id = r.id
       WHERE r.name = ?
       ORDER BY u.name
       """;
  1. Character Encoding
  • Always specify character encoding when reading/writing text
  • Use UTF-8 as the default encoding
  • Be explicit about encoding in file I/O and network operations

Spring Framework Practices

  1. Dependency Injection
  • Prefer constructor injection over field or setter injection
  • Keep components stateless when possible
  • Use qualifiers or named beans to resolve ambiguities
   // Good: Constructor injection
   @Service
   public class UserServiceImpl implements UserService {
       private final UserRepository userRepository;
       private final PasswordEncoder passwordEncoder;

       @Autowired // Optional in newer Spring versions
       public UserServiceImpl(UserRepository userRepository,
                            PasswordEncoder passwordEncoder) {
           this.userRepository = userRepository;
           this.passwordEncoder = passwordEncoder;
       }
   }

   // Avoid: Field injection
   @Service
   public class UserServiceImpl implements UserService {
       @Autowired
       private UserRepository userRepository;

       @Autowired
       private PasswordEncoder passwordEncoder;
   }
  1. Configuration Management
  • Use @Configuration classes instead of XML
  • Group configurations by functional area
  • Use profiles for environment-specific configuration
  • Externalize sensitive configuration (credentials, API keys)
   @Configuration
   @PropertySource("classpath:messaging.properties")
   public class MessagingConfiguration {

       @Bean
       @Profile("production")
       public MessageSender productionMessageSender() {
           return new KafkaMessageSender();
       }

       @Bean
       @Profile("development")
       public MessageSender developmentMessageSender() {
           return new InMemoryMessageSender();
       }
   }
  1. Spring Boot Best Practices
  • Use starter dependencies to reduce boilerplate
  • Leverage auto-configuration but understand what’s happening
  • Override only what you need to customize
  • Use application-{profile}.properties for profile-specific configuration
  • Prefer configuration properties classes over direct @Value injections
   @Configuration
   @ConfigurationProperties(prefix = "app.mail")
   @Validated
   public class MailProperties {
       @NotBlank
       private String host;

       @Min(1)
       @Max(65535)
       private int port = 25;

       private String username;
       private String password;

       // Getters and setters
   }

Database & Persistence

  1. JPA/Hibernate Best Practices
  • Use lazy loading judiciously and handle N+1 query problems
  • Define appropriate fetch strategies in queries
  • Configure proper cascade types (avoid CascadeType.ALL)
  • Use value types for complex attributes without identity
  • Set appropriate batch sizes for batch operations
   @Entity
   public class Order {
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;

       @ManyToOne(fetch = FetchType.LAZY)
       @JoinColumn(name = "customer_id")
       private Customer customer;

       @OneToMany(mappedBy = "order", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
       private List<OrderItem> items = new ArrayList<>();

       @Embedded
       private Address shippingAddress;

       // N+1 problem avoided with fetch join
       public static Optional<Order> findByIdWithItems(EntityManager em, Long id) {
           return em.createQuery(
                   "SELECT o FROM Order o " +
                   "LEFT JOIN FETCH o.items " +
                   "WHERE o.id = :id", Order.class)
               .setParameter("id", id)
               .getResultStream()
               .findFirst();
       }
   }
  1. Transaction Management
  • Use declarative transaction management with @Transactional
  • Set the appropriate isolation level for your use case
  • Keep transactions as short as possible
  • Be explicit about read-only transactions
  • Understand transaction propagation behaviors
   @Service
   public class OrderService {
       private final OrderRepository orderRepository;
       private final InventoryService inventoryService;

       // Constructor omitted for brevity

       @Transactional
       public Order createOrder(OrderRequest request) {
           // This method creates a new transaction
           Order order = new Order();
           // Set order properties

           return orderRepository.save(order);
       }

       @Transactional(readOnly = true)
       public List<Order> findOrdersByCustomer(Long customerId) {
           // Read-only optimization
           return orderRepository.findByCustomerId(customerId);
       }

       @Transactional(propagation = Propagation.REQUIRES_NEW)
       public void processRefund(Long orderId) {
           // Always creates a new transaction
           // even if called from another transactional method
       }
   }
  1. Native Queries
  • Specify the result class when using native queries
  • Use named parameters instead of positional parameters
  • Consider using projections for partial entity loading
   @Repository
   public class OrderRepositoryImpl implements OrderRepositoryCustom {
       @PersistenceContext
       private EntityManager entityManager;

       @Override
       public List<Order> findActiveOrdersByCustomer(Long customerId) {
           return entityManager.createNativeQuery(
               "SELECT o.* FROM orders o " +
               "WHERE o.customer_id = :customerId " +
               "AND o.status = 'ACTIVE'", Order.class)
               .setParameter("customerId", customerId)
               .getResultList();
       }

       @Override
       public List<OrderSummary> findOrderSummaries() {
           return entityManager.createNativeQuery(
               "SELECT o.id as orderId, o.order_date as orderDate, " +
               "c.name as customerName, SUM(oi.quantity * oi.price) as total " +
               "FROM orders o " +
               "JOIN customers c ON o.customer_id = c.id " +
               "JOIN order_items oi ON oi.order_id = o.id " +
               "GROUP BY o.id, o.order_date, c.name",
               "OrderSummaryMapping")
               .getResultList();
       }
   }

   @Entity
   @SqlResultSetMapping(
       name = "OrderSummaryMapping",
       classes = @ConstructorResult(
           targetClass = OrderSummary.class,
           columns = {
               @ColumnResult(name = "orderId", type = Long.class),
               @ColumnResult(name = "orderDate", type = LocalDate.class),
               @ColumnResult(name = "customerName", type = String.class),
               @ColumnResult(name = "total", type = BigDecimal.class)
           }
       )
   )
   public class Order {
       // Entity implementation
   }
  1. Database Access
  • Use prepared statements to prevent SQL injection
  • Use named parameters in queries for readability
  • Consider using JPA entity classes when appropriate

Concurrency & Multithreading

  1. Thread Safety
  • Make classes immutable when possible
  • Use concurrent collections from java.util.concurrent
  • Understand the proper use of synchronized, volatile, and atomic classes
  • Minimize shared mutable state
  • Document thread safety (or lack thereof) in class Javadoc
   /**
    * Thread-safe counter implementation.
    */
   public class Counter {
       private final AtomicLong count = new AtomicLong();

       public void increment() {
           count.incrementAndGet();
       }

       public long getCount() {
           return count.get();
       }
   }
  1. Modern Concurrency (Java 8+)
  • Use CompletableFuture for async operations
  • Understand the Fork/Join framework for parallel tasks
  • Use ExecutorService properly and shut it down
  • Consider thread pools and task executors for managing threads
   @Service
   public class ProductEnrichmentService {
       private final ExecutorService executor;
       private final PriceService priceService;
       private final InventoryService inventoryService;
       private final ReviewService reviewService;

       public ProductEnrichmentService(PriceService priceService,
                                     InventoryService inventoryService,
                                     ReviewService reviewService) {
           this.priceService = priceService;
           this.inventoryService = inventoryService;
           this.reviewService = reviewService;
           this.executor = Executors.newFixedThreadPool(10);
       }

       public CompletableFuture<EnrichedProduct> enrichProduct(Product product) {
           CompletableFuture<Price> priceFuture = 
               CompletableFuture.supplyAsync(() -> priceService.getPrice(product.getId()), executor);

           CompletableFuture<InventoryStatus> inventoryFuture = 
               CompletableFuture.supplyAsync(() -> inventoryService.getStatus(product.getId()), executor);

           CompletableFuture<List<Review>> reviewsFuture = 
               CompletableFuture.supplyAsync(() -> reviewService.getReviews(product.getId()), executor);

           return CompletableFuture.allOf(priceFuture, inventoryFuture, reviewsFuture)
               .thenApply(v -> new EnrichedProduct(
                   product,
                   priceFuture.join(),
                   inventoryFuture.join(),
                   reviewsFuture.join()
               ));
       }

       @PreDestroy
       public void shutdown() {
           executor.shutdown();
           try {
               if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                   executor.shutdownNow();
               }
           } catch (InterruptedException e) {
               executor.shutdownNow();
           }
       }
   }

RESTful API Design

  1. Resource Modeling
  • Design URLs around resources, not actions
  • Use appropriate HTTP methods (GET, POST, PUT, DELETE)
  • Use proper HTTP status codes
  • Provide API versioning strategy
   @RestController
   @RequestMapping("/api/v1/orders")
   public class OrderController {
       private final OrderService orderService;

       // Constructor omitted for brevity

       @GetMapping
       public List<OrderDto> getAllOrders() {
           return orderService.findAll();
       }

       @GetMapping("/{id}")
       public ResponseEntity<OrderDto> getOrder(@PathVariable Long id) {
           return orderService.findById(id)
               .map(ResponseEntity::ok)
               .orElse(ResponseEntity.notFound().build());
       }

       @PostMapping
       @ResponseStatus(HttpStatus.CREATED)
       public OrderDto createOrder(@Valid @RequestBody OrderRequest request) {
           return orderService.createOrder(request);
       }

       @PutMapping("/{id}")
       public ResponseEntity<OrderDto> updateOrder(
               @PathVariable Long id,
               @Valid @RequestBody OrderRequest request) {
           return ResponseEntity.ok(orderService.updateOrder(id, request));
       }

       @DeleteMapping("/{id}")
       @ResponseStatus(HttpStatus.NO_CONTENT)
       public void deleteOrder(@PathVariable Long id) {
           orderService.deleteOrder(id);
       }
   }
  1. API Documentation
  • Use Swagger/OpenAPI for API documentation
  • Document all endpoints, parameters, and possible responses
  • Include examples for better understanding “`java
    @RestController
    @RequestMapping("/api/v1/products")
    @Tag(name = "Product Management", description = "APIs for managing products")
    public class ProductController { @Operation(
    summary = "Get a product by ID",
    description = "Returns a single product by its unique identifier",
    responses = {
    @ApiResponse(
    responseCode = "200",
    description = "Product found",
    content = @Content(schema = @Schema(implementation = ProductDto.class))
    ),
    @ApiResponse(
    responseCode = "404",
    description = "Product not found",
    content = @Content(schema = @Schema(implementation = ErrorResponse.class))
    )
    }

Published by Marut Singh

Welcome to my blog. I am software architect, mentor, corporate trainer. Developing software for last 15 years in various domains..I work in different stacks and software architecture is my area of speciality..worked in c++, java, c#, scala, play vert.x, spring, nosql blah blah blah. And offcourse cloud technologies. Software Engineer cant imagine life without cloud :-) Always exploring new languages and tools to make sure that I do not loose touch, to ensure that I delivery high quality software in reasonable cost at all times. Love TDD, BDD, Agile and more than anything simplicity.. Normally I am very helpful so if you need some help do get in touch.

Leave a comment