Dirty Izgar Writing Close: A Clean Code Crash Course

by SkillAiNest

Dirty Izgar Writing Close: A Clean Code Crash Course
Photo by Author | Ideogram

If you have been coding in Azar for a while, you have probably mastered the basics, making some projects. And now you are seeing your code thinking: “It works, but … this is not something I proudly show in a code review.” We are all there.

But when you keep coding, writing a clean code is as important as writing the functional code. In this article, I have compiled a practical technique that can help you go from “it does not touch,” it is actually intact.

🔗 🔗 Link from the code on the Gut Hub

1. Model data clearly. Do not pass around the duct

The dictionaries are extremely flexible in Azar and this is a particularly problem. When you pass through the raw dictionary in your code, you are inviting types, key errors and confusion as to which data should actually exist.

Instead of:

def process_user(user_dict):
    if user_dict('status') == 'active':  # What if 'status' is missing?
        send_email(user_dict('email'))   # What if it's 'mail' in some places?
        
        # Is it 'name', 'full_name', or 'username'? Who knows!
        log_activity(f"Processed {user_dict('name')}")

This code is not strong because it assumes that the dictionary keys are available without verification. This offers no protection from types or lost keys, causing KeyError Run Times exceptions. There is no document that the fields are expected.

Do this:

from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    id: int
    email: str
    full_name: str
    status: str
    last_login: Optional(datetime) = None

def process_user(user: User):
    if user.status == 'active':
        send_email(user.email)
        log_activity(f"Processed {user.full_name}")

Of azagor @dataclass The decorator provides you with a clean structure with a minimal boiler plate. Your IDE can now automatically provide for attributes, and if the desired fields are missing, you will get instant mistakes.

Consider PY, PYDANTIC for more complex verification:

from pydantic import BaseModel, EmailStr, validator

class User(BaseModel):
    id: int
    email: EmailStr  # Validates email format
    full_name: str
    status: str
    
    @validator('status')
    def status_must_be_valid(cls, v):
        if v not in {'active', 'inactive', 'pending'}:
            raise ValueError('Must be active, inactive or pending')
        return v

Now your data itself verifies itself, catching mistakes soon, and clearly document expectations.

2. Use anams for known choice

Strings are the victims of literary types and no IDE is automatically provided. Verification is only on the run time.

Instead of:

def process_order(order, status):
    if status == 'pending':
        # process logic
    elif status == 'shipped':
        # different logic
    elif status == 'delivered':
        # more logic
    else:
        raise ValueError(f"Invalid status: {status}")
        
# Later in your code...
process_order(order, 'shiped')  # Typo! But no IDE warning

Do this:

from enum import Enum, auto

class OrderStatus(Enum):
    PENDING = 'pending'
    SHIPPED = 'shipped'
    DELIVERED = 'delivered'
    
def process_order(order, status: OrderStatus):
    if status == OrderStatus.PENDING:
        # process logic
    elif status == OrderStatus.SHIPPED:
        # different logic
    elif status == OrderStatus.DELIVERED:
        # more logic
    
# Later in your code...
process_order(order, OrderStatus.SHIPPED)  # IDE autocomplete helps!

When you are dealing with a fixed set of options, an Annum makes your code stronger and its own documentation.

With Anumus:

  • Your IDE provides automatic tips
  • Types (almost) become impossible
  • You can repeat through all possible values ​​when needed

Anum prepares a set of nominees. Sort of indication status: OrderStatus Expected parameter type documents. Using OrderStatus.SHIPPED Instead of the wire literally, IDE automatically allows and grabs the type of type at the time of development.

3. Use only keyword arguments for explanation

The flexible argument system is powerful, but when the function calls contain several optional parameters, it can cause confusion.

Instead of:

def create_user(name, email, admin=False, notify=True, temporary=False):
    # Implementation
    
# Later in code...
create_user("John Smith", "john@example.com", True, False)

Wait, what do these bolds mean again?

When called with location arguments, it is unclear what the Bolian values ​​represent without examining the definition of the function. Is true for admin, inform, or anything else?

Do this:

def create_user(name, email, *, admin=False, notify=True, temporary=False):
    # Implementation

# Now you must use keywords for optional args
create_user("John Smith", "john@example.com", admin=True, notify=False)

*, Syntax forces all the arguments then by keywords. This shows your function itself and prevents the problem of “mystery bolin” where readers cannot tell what is right or wrong without reading the definition of the function.

This sample is especially useful in API calls and so on, where you want to ensure the explanation on the call site.

4. Use Pathlib on os.path

The OS.Path module of Azigar is active but clunky. The new Pathalib Module provides an object -based approach that is more intuitive and less error.

Instead of:

import os

data_dir = os.path.join('data', 'processed')
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

filepath = os.path.join(data_dir, 'output.csv')
with open(filepath, 'w') as f:
    f.write('results\n')
    
# Check if we have a JSON file with the same name
json_path = os.path.splitext(filepath)(0) + '.json'
if os.path.exists(json_path):
    with open(json_path) as f:
        data = json.load(f)

It uses string manipulation with os.path.join() And os.path.splitext() To deal with the path. The works of the path are scattered in various functions. The code is functional and less intuitive.

Do this:

from pathlib import Path

data_dir = Path('data') / 'processed'
data_dir.mkdir(parents=True, exist_ok=True)

filepath = data_dir / 'output.csv'
filepath.write_text('results\n')

# Check if we have a JSON file with the same name
json_path = filepath.with_suffix('.json')
if json_path.exists():
    data = json.loads(json_path.read_text())

Why the Pethalib is better:

  • Is more intuitive / to join with it is more intuitive
  • Ways such as mkdir()For, for, for,. exists()And read_text() The pathways are connected to the Object
  • Works such as changing the extensions (with_Sufx) are more meaningful

Pathalib handles the path of manipulation in various operating systems. This makes your code more portable and stronger.

5. Failure to faster with the guard clauses

If it is often difficult to understand and maintain statements in a deep nest. Early Return – Guard Classes Use – leads to high reading code.

Instead of:

def process_payment(order, user):
    if order.is_valid:
        if user.has_payment_method:
            payment_method = user.get_payment_method()
            if payment_method.has_sufficient_funds(order.total):
                try:
                    payment_method.charge(order.total)
                    order.mark_as_paid()
                    send_receipt(user, order)
                    return True
                except PaymentError as e:
                    log_error(e)
                    return False
            else:
                log_error("Insufficient funds")
                return False
        else:
            log_error("No payment method")
            return False
    else:
        log_error("Invalid order")
        return False

It is difficult to follow the deep nest. In each conditional block, several branches are needed simultaneously.

Do this:

def process_payment(order, user):
    # Guard clauses: check preconditions first
    if not order.is_valid:
        log_error("Invalid order")
        return False
        
    if not user.has_payment_method:
        log_error("No payment method")
        return False
    
    payment_method = user.get_payment_method()
    if not payment_method.has_sufficient_funds(order.total):
        log_error("Insufficient funds")
        return False
    
    # Main logic comes after all validations
    try:
        payment_method.charge(order.total)
        order.mark_as_paid()
        send_receipt(user, order)
        return True
    except PaymentError as e:
        log_error(e)
        return False

The guard’s clauses handle error cases, which reduces the level of inscription. Each condition is examined in order, making it easier to follow the flow. The central logic comes to the end, which is clearly separated from the error.

This approach is much better because your logic increases in complexity.

6. Do not use the list of the list more

Understanding of the list is one of the most beautiful features of Azigar, but they are able to read when complex conditions or changes are overloaded.

Instead of:

# Hard to parse at a glance
active_premium_emails = (user('email') for user in users_list 
                         if user('status') == 'active' and 
                         user('subscription') == 'premium' and 
                         user('email_verified') and
                         not user('email') in blacklisted_domains)

The understanding of this list packs a lot of logic in one line. It’s difficult to read and debug. Many conditions are tied together, making it difficult to understand the quality of the filter.

Do this:
Here are the best alternatives.

Option 1: Work with a descriptive name

The complex state draws out in a designated function with a descriptive name. The understanding of the list is now more pronounced, focusing on what it (removing emails) is instead of filtering instead.

def is_valid_premium_user(user):
    return (user('status') == 'active' and
            user('subscription') == 'premium' and
            user('email_verified') and
            not user('email') in blacklisted_domains)

active_premium_emails = (user('email') for user in users_list if is_valid_premium_user(user))

Option 2: Traditional loop when the logic is complicated

Initially uses a traditional loop that continues to explain. Each condition is examined separately, making it easier to debug on which condition is failing. The logic of change is also clearly different.

active_premium_emails = ()
for user in users_list:
    # Complex filtering logic
    if user('status') != 'active':
        continue
    if user('subscription') != 'premium':
        continue
    if not user('email_verified'):
        continue
    if user('email') in blacklisted_domains:
        continue
        
    # Complex transformation logic
    email = user('email').lower().strip()
    active_premium_emails.append(email)

List understanding should be enabled to read your code more, not less. When the logic becomes complicated:

  • Break the complex conditions in designated functions
  • Consider the use of a regular loop with the ongoing continuing
  • Divide complex operations into multiple stages

Remember, the purpose is to read.

7. Write the Pure Pure functions again

A function is a pure function if he always produces the same output for the same inputs. Also, it has no side effects.

Instead of:

total_price = 0  # Global state

def add_item_price(item_name, quantity):
    global total_price
    # Look up price from global inventory
    price = inventory.get_item_price(item_name)
    # Apply discount 
    if settings.discount_enabled:
        price *= 0.9
    # Update global state
    total_price += price * quantity
    
# Later in code...
add_item_price('widget', 5)
add_item_price('gadget', 3)
print(f"Total: ${total_price:.2f}")

It uses a global state (total_price) Which makes the test difficult.

The side effects of the function are (to modify the global state) and it depends on the external state (inventory and settings). This makes it unexpected and difficult to reuse.

Do this:

def calculate_item_price(item, price, quantity, discount=0):
    """Calculate final price for a quantity of items with optional discount.
    
    Args:
        item: Item identifier (for logging)
        price: Base unit price
        quantity: Number of items
        discount: Discount as decimal 
        
    Returns:
        Final price after discounts
    """
    discounted_price = price * (1 - discount)
    return discounted_price * quantity

def calculate_order_total(items, discount=0):
    """Calculate total price for a collection of items.
    
    Args:
        items: List of (item_name, price, quantity) tuples
        discount: Order-level discount
        
    Returns:
        Total price after all discounts
    """
    return sum(
        calculate_item_price(item, price, quantity, discount)
        for item, price, quantity in items
    )

# Later in code...
order_items = (
    ('widget', inventory.get_item_price('widget'), 5),
    ('gadget', inventory.get_item_price('gadget'), 3),
)

total = calculate_order_total(order_items, 
                             discount=0.1 if settings.discount_enabled else 0)
print(f"Total: ${total:.2f}")

The following version uses pure functions that take all dependence as parameters.

8. Write documents for public functions and classes

The documents are not (and should not be) after one thought. This is the main part of the maintaining code. Good DOC documents not only tell what works, but also why they are present and how to use them properly.

Instead of:

def celsius_to_fahrenheit(celsius):
    """Convert Celsius to Fahrenheit."""
    return celsius * 9/5 + 32

This is a minimum document that only repeats the function name. Provides any information about parameters, return values, or edge matters.
Do this:

def celsius_to_fahrenheit(celsius):
	"""
	Convert temperature from Celsius to Fahrenheit.
	The formula used is: F = C × (9/5) + 32
	Args:
    	celsius: Temperature in degrees Celsius (can be float or int)
	Returns:
    	Temperature converted to degrees Fahrenheit
	Example:
    	>>> celsius_to_fahrenheit(0)
    	32.0
    	>>> celsius_to_fahrenheit(100)
    	212.0
    	>>> celsius_to_fahrenheit(-40)
    	-40.0
	"""
	return celsius * 9/5 + 32

A good docstring:

  • Documents parameters and withdrawal values
  • Notes any exception that may be lifted
  • Provides examples of use

Your dostings act as a viable document that lives in harmony with your code.

9. Automatic lining and formatting

Do not rely on styling problems and manual inspection to catch ordinary insects. Automatic tools can handle the painful work to ensure the quality and consistency of the code.

You can try to configure these lining and formatting tools:

  1. Black – code formator
  2. Leopard – sharp linter
  3. MyPY – static type checker
  4. isort – Imported Organizer

Connect them using Pre -Committee Hooks to automatically check and form the code before each covenant:

  1. Pre -Committee Install: Code styling = “Background: #F5F5F5;”> Install PIP Pre -Committee
  2. Make a code styling = “Background: #F5F5F5;”>. Pre-commit-config.yaml filed with file tools
  3. Run Code Style = “Background: #F5F5F5;”> Install to activate pre -commit

This setup ensures permanent code style and catchs mistakes soon without manual effort.

You can check 7 tools to help you write better codes to find out more.

10. Except for the catch to avoid

The general exception handlers hide the insects and make debugging difficult. They catch everything, including syntax mistakes, memory errors, and keyboard barriers.

Instead of:

try:
    user_data = get_user_from_api(user_id)
    process_user_data(user_data)
    save_to_database(user_data)
except:
    # What failed? We'll never know!
    logger.error("Something went wrong")

It uses a naked exception to handle:

  • Programming errors (such as syntax errors)
  • System errors (like memory.)
  • Keyboard interference (CTRL+C)
  • Expected errors (such as a network timout)

This makes debugging extremely difficult, as all the mistakes are treated the same.

Do this:

try:
    user_data = get_user_from_api(user_id)
    process_user_data(user_data)
    save_to_database(user_data)
except ConnectionError as e:
    logger.error(f"API connection failed: {e}")
    # Handle API connection issues
except ValueError as e:
    logger.error(f"Invalid user data received: {e}")
    # Handle validation issues
except DatabaseError as e:
    logger.error(f"Database error: {e}")
    # Handle database issues
except Exception as e:
    # Last resort for unexpected errors
    logger.critical(f"Unexpected error processing user {user_id}: {e}", 
                  exc_info=True)
    # Possibly re-raise or handle generically
    raise

Catching specific exceptions that can be expected and can be handled properly. Each exception type has its own error message and handling strategy.

The final holds the final unpredictable mistakes except discount, logs them with full traceback (exc_info=True), And raises them again to avoid serious issues.

If you need a catch all -handler for some reason, use except Exception as e: Instead of naked except:And always log in with full details of exception exc_info=True.

Wrap

I hope you have to use at least some of these methods in your code. Start implementing them in your projects.

It will be easier for you to maintain your code, more capable, and argue about it.

Next time you have the temptation to take a shortcut, remember: The code is read more times than his writing. Happy Clear coding?

Pray Ca Is a developer and technical author from India. She likes to work at the intersection of mathematics, programming, data science, and content creation. The fields of interest and expertise include dupas, data science, and natural language processing. She enjoys reading, writing, coding and coffee! Currently, they are working with the developer community to learn and share their knowledge with the developer community by writing a lesson, how to guide, feed and more. The above resources review and coding also engages lessons.

You may also like

Leave a Comment

At Skillainest, we believe the future belongs to those who embrace AI, upgrade their skills, and stay ahead of the curve.

Get latest news

Subscribe my Newsletter for new blog posts, tips & new photos. Let's stay updated!

@2025 Skillainest.Designed and Developed by Pro