Skip to content

General Programming Principles

This guide covers universal programming principles that apply across all programming languages and development scenarios.

🎯 Fundamental Principles

DRY (Don't Repeat Yourself)

Avoid duplicating code or logic throughout your application.

Problem

# Bad: Repeated logic
def calculate_discount_price(price, discount_percent):
    discounted_price = price - (price * discount_percent / 100)
    return discounted_price

def calculate_tax_price(price, tax_percent):
    taxed_price = price + (price * tax_percent / 100)
    return taxed_price

# Similar calculation logic repeated

Solution

# Good: Extract common logic
def calculate_percentage_adjustment(base_price, adjustment_percent):
    return base_price + (base_price * adjustment_percent / 100)

def calculate_discount_price(price, discount_percent):
    return calculate_percentage_adjustment(price, -discount_percent)

def calculate_tax_price(price, tax_percent):
    return calculate_percentage_adjustment(price, tax_percent)

KISS (Keep It Simple, Stupid)

Write simple, readable code that's easy to understand and maintain.

Problem

// Bad: Overly complex
public boolean isValidEmail(String email) {
    return email != null && 
           email.contains("@") && 
           email.indexOf("@") == email.lastIndexOf("@") &&
           email.indexOf(".") > email.indexOf("@") &&
           email.lastIndexOf(".") < email.length() - 2;
}

Solution

// Good: Simple and clear
public boolean isValidEmail(String email) {
    if (email == null) return false;

    int atIndex = email.indexOf("@");
    int lastDotIndex = email.lastIndexOf(".");

    return atIndex > 0 && 
           atIndex == email.lastIndexOf("@") &&
           lastDotIndex > atIndex &&
           lastDotIndex < email.length() - 2;
}

// Better: Use regex or email validation library
public boolean isValidEmail(String email) {
    return email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}

SOLID Principles

Five object-oriented design principles for maintainable software.

S - Single Responsibility Principle

A class should have only one reason to change.

# Bad: Multiple responsibilities
class User:
    def save_to_database(self):
        # Database logic
        pass

    def send_email(self):
        # Email logic
        pass

    def validate_input(self):
        # Validation logic
        pass

# Good: Single responsibility
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

class UserRepository:
    def save(self, user):
        # Database logic only
        pass

class EmailService:
    def send_welcome_email(self, user):
        # Email logic only
        pass

class UserValidator:
    def validate(self, user):
        # Validation logic only
        pass

O - Open/Closed Principle

Software entities should be open for extension, closed for modification.

# Bad: Need to modify for new shapes
class AreaCalculator:
    def calculate_area(self, shape):
        if shape.type == "circle":
            return 3.14 * shape.radius ** 2
        elif shape.type == "rectangle":
            return shape.width * shape.height
        # Need to modify for new shapes!

# Good: Open for extension
class Shape:
    def area(self):
        raise NotImplementedError

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class AreaCalculator:
    def calculate_area(self, shape):
        return shape.area()  # Works for any shape

YAGNI (You Aren't Gonna Need It)

Don't add functionality until you actually need it.

Problem

// Bad: Adding features "just in case"
public class UserService {
    public void createUser(User user) { /* ... */ }
    public void updateUser(User user) { /* ... */ }
    public void deleteUser(User user) { /* ... */ }
    public void exportToCSV(User user) { /* ... */ }  // Not needed yet
    public void exportToXML(User user) { /* ... */ }  // Not needed yet
    public void exportToJSON(User user) { /* ... */ }  // Not needed yet
}

Solution

// Good: Only implement what's needed now
public class UserService {
    public void createUser(User user) { /* ... */ }
    public void updateUser(User user) { /* ... */ }
    public void deleteUser(User user) { /* ... */ }

    // Add export features when actually needed
}

🔧 Code Quality Principles

Readability

Code should be written for humans to read, not just for computers to execute.

Guidelines

  • Meaningful Names: Use descriptive variable and function names
  • Consistent Style: Follow language conventions consistently
  • Proper Comments: Explain why, not what
  • Logical Organization: Group related code together

Example

# Bad: Unclear code
def p(x, y):
    r = []
    for i in range(len(x)):
        if x[i] > y:
            r.append(x[i])
    return r

# Good: Clear and readable
def filter_greater_than_threshold(numbers, threshold):
    """Return numbers greater than the given threshold."""
    filtered_numbers = []
    for number in numbers:
        if number > threshold:
            filtered_numbers.append(number)
    return filtered_numbers

# Better: Use built-in functions
def filter_greater_than_threshold(numbers, threshold):
    """Return numbers greater than the given threshold."""
    return [num for num in numbers if num > threshold]

Maintainability

Code should be easy to modify and extend.

Guidelines

  • Small Functions: Each function does one thing well
  • Loose Coupling: Minimize dependencies between components
  • High Cohesion: Group related functionality together
  • Configuration: Externalize configuration values

Example

# Bad: Hard to maintain
def process_data():
    # Database connection hardcoded
    conn = sqlite3.connect("/path/to/database.db")

    # Business logic mixed with data access
    data = conn.execute("SELECT * FROM users WHERE age > 18").fetchall()

    # Processing logic mixed with output
    for row in data:
        if row[3] > 1000:  # Magic number
            print(f"High value user: {row[1]}")
        else:
            print(f"Regular user: {row[1]}")

# Good: Easy to maintain
def process_data():
    config = load_config()
    db_connection = create_database_connection(config.database_url)

    adult_users = get_adult_users(db_connection)
    process_user_list(adult_users, config.high_value_threshold)

def get_adult_users(connection):
    return connection.execute(
        "SELECT * FROM users WHERE age > ?", 
        (18,)
    ).fetchall()

def process_user_list(users, high_value_threshold):
    for user in users:
        if user.balance > high_value_threshold:
            print(f"High value user: {user.name}")
        else:
            print(f"Regular user: {user.name}")

Testability

Code should be easy to test.

Guidelines

  • Pure Functions: Avoid side effects when possible
  • Dependency Injection: Pass dependencies as parameters
  • Single Responsibility: Easy to test small, focused functions
  • Mock Dependencies: Use test doubles for external dependencies

Example

# Bad: Hard to test (depends on external API)
def get_user_data(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

# Good: Easy to test (dependency injection)
def get_user_data(user_id, http_client):
    response = http_client.get(f"https://api.example.com/users/{user_id}")
    return response.json()

# Test
class MockHttpClient:
    def get(self, url):
        return MockResponse({"id": 123, "name": "Test User"})

def test_get_user_data():
    mock_client = MockHttpClient()
    user_data = get_user_data(123, mock_client)
    assert user_data["name"] == "Test User"

🎯 Design Patterns

Strategy Pattern

Encapsulate algorithms and make them interchangeable.

# Strategy interface
class SortingStrategy:
    def sort(self, data):
        raise NotImplementedError

# Concrete strategies
class BubbleSort(SortingStrategy):
    def sort(self, data):
        # Bubble sort implementation
        return sorted_data

class QuickSort(SortingStrategy):
    def sort(self, data):
        # Quick sort implementation
        return sorted_data

# Context
class Sorter:
    def __init__(self, strategy):
        self.strategy = strategy

    def sort_data(self, data):
        return self.strategy.sort(data)

# Usage
sorter = Sorter(QuickSort())
result = sorter.sort_data([3, 1, 4, 1, 5])

Factory Pattern

Create objects without specifying exact classes.

# Product interface
class DatabaseConnection:
    def connect(self):
        raise NotImplementedError

# Concrete products
class MySQLConnection(DatabaseConnection):
    def connect(self):
        return "Connected to MySQL"

class PostgreSQLConnection(DatabaseConnection):
    def connect(self):
        return "Connected to PostgreSQL"

# Factory
class DatabaseConnectionFactory:
    @staticmethod
    def create_connection(db_type):
        if db_type == "mysql":
            return MySQLConnection()
        elif db_type == "postgresql":
            return PostgreSQLConnection()
        else:
            raise ValueError(f"Unknown database type: {db_type}")

# Usage
connection = DatabaseConnectionFactory.create_connection("mysql")
connection.connect()

🔗 Language-Specific Best Practices