Write Better Functions

Paulund
7 min readFeb 25, 2023

--

As a software developer, writing good functions is an essential part of creating maintainable and reliable code.

In this blog post, we’ll look at several tips for writing better functions.

Make your functions small and focused

One of the keys to writing good functions is to keep them small and focused. Smaller functions are easier to understand, test, and maintain. They are also less likely to contain bugs and are more reusable.

A good rule of thumb is to aim for functions that are no longer than 20 lines of code. Of course, this is just a guideline and there will be cases where it makes sense to have longer functions.

However, if you find that your functions are getting too long, it may be a good idea to break them up into smaller, more focused functions.

Single Responsibility

Another way to keep your functions small and focused is to give them a single responsibility or job.

This means that a function should do one thing and do it well. For example, a function that retrieves data from a database and formats it for display is doing two things and would be better off as two separate functions.

Give your functions descriptive names

Choosing descriptive names for your functions is an important way to make your code more readable and easier to understand. A good function name should accurately describe what the function does and should be easy to understand even out of context.

For example, a function named fetchAndFormatData() is much clearer than a function named doSomething(). Descriptive names also make it easier for other developers to understand and use your code.

Use clear and consistent function signatures

The function signature includes the name of the function, the number and types of arguments, and the return type. Having clear and consistent function signatures is important for making your code easy to understand and use.

For example, consider the following function:

function calculateTotal(array $items, $taxRate, $discount = 0): float
{
// code to calculate the total
}

In this example, the function is named calculateTotal() and it takes three arguments: an array of items, a tax rate, and an optional discount. The function also has a clear return type (in this case, we can assume that it returns a number).

By following a consistent style for your function signatures, you can make your code easier to read and understand.

Use type hints

Type hints are a way to specify the expected type of an argument or return value in a function signature. By using type hints, you can make your code more readable and easier to understand.

Here is an example of how you can use type hints to write better functions in PHP:

class User
{
private $name;
private $age;
public function __construct(string $name, int $age)
{
$this->name = $name;
$this->age = $age;
}
public function getName(): string
{
return $this->name;
}
public function getAge(): int
{
return $this->age;
}
}

function getUsersOlderThan(int $age, array $users): array
{
$filteredUsers = [];
foreach ($users as $user) {
if ($user->getAge() > $age) {
$filteredUsers[] = $user;
}
}
return $filteredUsers;
}

$users = [
new User('John', 30),
new User('Jane', 25),
new User('Bob', 35),
];
$olderUsers = getUsersOlderThan(30, $users);

In this example, the getUsersOlderThan() function has a type hint for the $age argument (int) and the $users argument (array). It also has a type hint for the return value (array). This helps to make the function signature more readable and easier to understand.

By using type hints, you can help ensure that your functions are called with the correct types of arguments and can help prevent runtime errors. It is generally a good idea to use type hints whenever possible, especially for complex or critical functions.

Document your functions

Comments are an important tool for documenting your code and explaining how it works. When writing functions, it’s a good idea to include comments that explain what the function does and how it should be used.

In this example, the comments provide a clear explanation of what the function does and how it should be used. This makes it easier for other developers to understand and use the function.

Use appropriate levels of abstraction

Using appropriate levels of abstraction is an important aspect of writing good functions. Abstraction refers to the idea of separating the implementation details of a piece of code from its interface. By using appropriate levels of abstraction, you can make your functions more modular, flexible, and easier to understand.

Here is an example of how you can use appropriate levels of abstraction to write better functions in PHP:

// Low-level function that retrieves data from a database
function fetchDataFromDatabase($query)
{
// code to connect to the database and execute the query
// ...
// return the results
return $results;
}

// High-level function that formats data for display
function formatDataForDisplay(array $data)
{
// code to format the data
// ...
// return the formatted data
return $formattedData;
}

// Another high-level function that uses the low-level function and the high-level function
function fetchAndFormatData($query)
{
$data = fetchDataFromDatabase($query);
$formattedData = formatDataForDisplay($data);
return $formattedData;
}

In this example, the fetchDataFromDatabase() function is a low-level function that retrieves data from a database. The formatDataForDisplay() function is a high-level function that formats data for display. The fetchAndFormatData() function is another high-level function that uses the low-level fetchDataFromDatabase() function and the high-level formatDataForDisplay() function to fetch and format data in one step.

By using appropriate levels of abstraction, we can make our code more modular and flexible. The fetchAndFormatData() function can be easily modified or replaced without affecting the low-level fetchDataFromDatabase() function or the high-level formatDataForDisplay() function.

Avoid duplication

Avoiding duplication is an important aspect of writing good functions. Duplicate code is harder to maintain and more prone to errors. It is generally better to reuse code where appropriate and to use functions to encapsulate common logic.

Here is an example of how you can avoid duplication to write better functions in PHP:

// Function to calculate the total cost of a list of items
function calculateTotalCost(array $items)
{
$total = 0;
foreach ($items as $item) {
$total += $item['price'];
}
return $total;
}

// Function to calculate the total tax for a list of items
function calculateTotalTax(array $items, $taxRate)
{
$total = calculateTotalCost($items);
return $total * $taxRate;
}

// Function to calculate the total cost including tax for a list of items
function calculateTotalCostIncludingTax(array $items, $taxRate)
{
$total = calculateTotalCost($items);
$tax = calculateTotalTax($items, $taxRate);
return $total + $tax;
}

In this example, the calculateTotalCost() function is used to calculate the total cost of a list of items. This function is then reused in the calculateTotalTax() function and the calculateTotalCostIncludingTax() function to avoid duplication of the code to calculate the total cost.

By using functions to encapsulate common logic, we can make our code more modular and easier to maintain. If we need to change the way the total cost is calculated, we only need to modify the calculateTotalCost() function.

Follow best practices for error handling

Error handling is an important aspect of writing good functions. Proper error handling can help prevent bugs and make your code more reliable.

Here are some best practices for error handling in PHP:

  • Use exceptions for error handling: Exceptions are a powerful tool for handling errors in PHP. They allow you to separate the error handling logic from the normal flow of your code and to throw an exception whenever an error occurs.
  • Use descriptive error messages: Make sure your error messages are descriptive and provide enough information to help debug the problem.
  • Use appropriate exception types: Use specific exception types whenever possible. For example, use a FileNotFoundException when a file is not found, rather than a generic Exception.
  • Catch exceptions at the appropriate level: Don’t catch exceptions too low in the call stack. Instead, catch them at the highest level possible and handle the error in a way that makes sense for the context.
  • Don’t suppress errors: Avoid using the @ operator to suppress errors. This can hide important information and make it harder to debug problems.

Here is an example of how to follow these best practices when writing functions in PHP:

In this example, the readFile() function throws a FileNotFoundException if the file does not exist and a generic Exception if there is an error reading the file. The try/catch block.

Abstract Switch Statements Into Factories

One way to abstract a switch statement is to use a factory function. A factory function is a function that creates and returns objects based on some input.

Here is an example of how you can use a factory function to abstract a switch statement in PHP:

interface Shape
{
public function getArea();
}

class Circle implements Shape
{
private $radius;
public function __construct($radius)
{
$this->radius = $radius;
}
public function getArea()
{
return pi() * pow($this->radius, 2);
}
}

class Rectangle implements Shape
{
private $width;
private $height;
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
public function getArea()
{
return $this->width * $this->height;
}
}

function createShape($type, $arg1, $arg2 = null)
{
switch ($type) {
case 'circle':
return new Circle($arg1);
case 'rectangle':
return new Rectangle($arg1, $arg2);
default:
throw new Exception('Invalid shape type');
}
}

$shape = createShape('circle', 5);
echo $shape->getArea();

$shape = createShape('rectangle', 10, 20);
echo $shape->getArea();

In this example, the createShape() function is a factory function that creates and returns objects that implement the Shape interface. It takes a$type argument and two optional arguments, depending on the type of shape being created. The factory function uses a switch statement to determine which type of shape to create, and then returns an instance of the appropriate class.

By using a factory function, we can abstract the switch statement and make our code more modular and flexible. If we need to add a new type of shape, we can simply add a new case to the switch statement, without affecting the rest of the code.

Originally published at https://paulund.co.uk.

--

--

Paulund
Paulund

No responses yet