Using AI for Development the Right Way

This post cuts through the hype surrounding AI in development. We’ll explore how AI can be your secret weapon for conquering repetitive tasks like unit tests and documentation, debunking misconceptions and showing you how to leverage its power effectively. Learn how to automate the tedious and free yourself to focus on the creative aspects of coding, ultimately supercharging your development workflow and boosting your productivity!

Introduction

Imagine a world where unit tests write themselves and documentation magically appears. Sounds like science fiction, right? Well, with the rise of Artificial Intelligence (AI) in development, that world is closer than you think. But hold on, before you ditch your keyboard and declare yourself obsolete, let’s be clear: AI isn’t here to replace you. It’s here to empower you.

This post dives deep into the practical applications of AI for developers. We’ll debunk the misconceptions and myth of AI as a silver bullet, and instead, show you how it can be your powerful development sidekick. Learn how to leverage AI to automate tedious tasks, reclaim your time, and finally free yourself to focus on what you do best: building awesome software.

Setting Expectations: AI - The Helpful Partner, Not Coding Overlord

There’s no denying the buzz around AI in development. It’s like the early days of the internet - a future full of possibilities. This excitement is great, but it’s important to remember that AI is a powerful tool, not a replacement for developers.

Here’s the hype in a nutshell for you to understand it with easiness: AI can automate repetitive tasks, analyze massive datasets to uncover hidden patterns, and even generate code. This translates to faster development cycles, more efficient use of resources, and potentially even more creative solutions.

However, AI isn’t here to steal developer jobs. Here’s why:

  • Human expertise still reigns supreme: AI excels at following patterns and completing specific tasks, but it lacks the critical thinking, problem-solving, and creativity that developers bring to the table.
  • Understanding the why behind the what: AI can write code, but developers understand the underlying logic, purpose, and potential consequences. This big-picture thinking is crucial for building robust and maintainable systems.
  • The human touch matters: the best user experience often involve a deep understanding of human needs and desires. This is where developers excel - translating those needs into a functional and intuitive product.

In short, AI is posed to be a game-changer for developers, but it’s a sidekick, not a superhero. The future of development lies in humans and AI working together to create even more amazing things.

Understanding AI’s Capabilities

AI is bringing a wave of new possibilities to the development world, acting as a powerful assistant that streamlines processes and frees developers for more strategic tasks. Here are some ways AI is making a splash:

  • Automating repetitive tasks: AI can write unit tests, a crucial but often tedious part of development. This frees developers to focus on more complex integration and system-level testing.
  • Generating code and documentation: AI can analyze existing code and generate documentation, saving developers hours spent writing comments and explanations. Similarly, some AI tools can even refactor existing code and suggest code snippets or complete small functions based on developer input.
  • Uncovering hidden insights: AI can analyze massive datasets to identify patterns and trends that might be missed by humans. This can be invaluable for optimizing applications, predicting user behavior, and identifying potential bugs.
  • Personalization power: AI can personalize the development experience, suggesting relevant code libraries, debugging tools, and solutions based on the developer’s specific project and coding style.

However, it’s important to remember that AI isn’t a magic bullet. Here are some limitations to consider:

  • Not perfect, requires oversight: AI-generated code or documentation might contain errors or not perfectly capture the developer’s intent. Human review and refinement are still essential.
  • Limited creativity: as we said before, AI excels at following patterns, not coming up with entirely new ideas. Developers are still irreplaceable for conceptualizing innovative solutions and tackling unique challenges.
  • Bias creep: AI algorithms can inherit biases from the data they’re trained on. Developers need to be aware of this and take steps to mitigate bias in their AI-powered tools.
  • Black box problem: some AI models can be opaque, making it difficult to understand how they arrived at a particular suggestion. This can hinder debugging and troubleshooting.

Leveraging AI for Developer Productivity

AI tools like Google’s Gemini (which is the one I’m actively using) and Codellama are transforming the development landscape by assisting with tasks that can be time-consuming or tedious. Here’s how you can leverage them for unit testing and documentation generation:

Unit Testing with AI

Gemini

  • Highlight the code: select the specific code block you want to test within your code editor.
  • Access Gemini Code Assist or use the web: you can either use the Gemini Code Assist extension in your preferred code editor or IDE, or prefer to use the web interface of Gemini.
  • Prompt for tests: use clear prompts like “Write unit tests for this code” or “Generate unit test cases”. Gemini will analyze the code structure and functionality, suggesting relevant test cases.
  • Review and refine: review the generated tests, add specific edge cases or scenarios you want covered, and refine them as needed.

Codellama

  • Integrate the model: follow Codellama’s instructions to properly set it up with ollama.
  • Generate tests: prompt Codellama to generate unit tests (you can take a look at the how to prompt codellama blog post from the ollama website). Codellama will analyze the code and generate test cases based on its understanding.
  • Customize and run: review the generated tests, and specific scenarios, and then run them within your code editor’s testing framework.

Documentation generation with AI

  • Focus on the readability: ensure your code is well-commented and readable, as Gemini and Codellama uses comments to improve its understanding of the functionality and purpose of the code.
  • Generate documentation: use prompts like “Write documentation for this function”. AI will analyze the code structure, functionality and comments to suggest accurate documentation.
  • Review and expand: review the generated documentation and add additional details or explanations as needed to provide context, user examples and clear instructions.

Some recommendations:

  • AI-generated outputs are suggestions and starting points, not a finished product.
  • Human review and adjustments are crucial for both unit tests and documentation.
  • Ensure your code is well-commented and uses clear variable names for optimal AI understanding.

Real world example

Lately, I’ve been using Gemini to generate unit tests and documentation in my new project Norgolith, which is a monolithic Norg static site generator built with Rust. In this section we will see some examples of prompts and the tests and documentation generated by Gemini.

Unit tests generation

Let’s start with a simple example with the shortest module of the project (src/net.rs) of which we seek to have unit tests.

net.rs code

To do this, we copy the code and create the prompt for Gemini:

net.rs unit test prompt

Note the structure of the prompt:

  • We asked Gemini to create tests for Rust code to provide it with more context about the language that is being used so Gemini doesn’t have to induce it, in order to speed things up.
  • We leave spaces between the request and the code to improve the clarity of the prompt.
  • Finally, we paste the code with which we want to obtain the results.

Then, we wait for the AI to do its job, to which it returns the following Rust code:

net.rs unit test results

In addition, it also gives us an explanation of the code and its limitations:

net.rs unit test results explanation

However, if you are someone experienced with Rust you may have noticed that there are a few things wrong with the code. And if you are not, here I will explain to you to give rise to the point where we talked about that AI is not perfect and that it needs human intervention.

  1. We don’t need to import tokio::test, as we can use it directly with the #[tokio::test] syntax.
  2. The #[async_test] attribute is invalid, so the code will not work.
  3. A new import of std::net is made for ToSocketAddr, which is not used in the code.
  4. In the generated code, the internal module tests is not being used nor are the testing attributes #[cfg(test)] being used.
  5. One of the tests (for an unused port) can be greatly simplified.
  6. It seems that there is no need to close the TCP listener (in fact, my rust-analyzer couldn’t find the close() method for them).

Therefore, after fixing those nitpicks and making some small changes related to GitHub CI (not relevant for the prompt), the final code looks like this:

use std::net::TcpListener;

/// Check if the given port is available or busy
pub fn is_port_available(port: u16) -> bool {
    TcpListener::bind(("127.0.0.1", port)).is_ok()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    #[cfg_attr(feature = "ci", ignore)]
    async fn test_is_port_available_unused_port() {
        assert!(is_port_available(3030));
    }

    #[tokio::test]
    #[cfg_attr(feature = "ci", ignore)]
    async fn test_is_port_available_used_port() {
        let listener = TcpListener::bind("localhost:8080").unwrap();
        let port = listener.local_addr().unwrap().port();

        assert!(!is_port_available(port));
    }
}

Then, we run the code and indeed, these tests work correctly:

net.rs tests run

Documentation generation

Similarly, we provide code and ask you to generate documentation for it (in this case, I generated documentation for the src/cli.rs functions):

cli.rs documentation

You can find the documented code here in case you want to take a look at it.

Note: some popular projects in the open-source community have also benefited from AI-generated documentation, such as bob, a version manager for the Neovim code editor.

Refactoring codebase

We can also ask AIs to help us refactoring some nasty code in order to improve some aspects of it, like the readability. Below we have an example, but first of all let’s take a look at the code before the AI refactoring:

/// Asynchronously parse the command-line arguments and executes the corresponding subcommand
pub async fn start() -> Result<()> {
   let cli = Cli::parse();

   match &cli.command {
       Commands::Init { name } => {
           if let Some(value) = name {
               cmd::init(value).await?;
           } else {
               return Err(anyhow!("Missing name for the site")
                   .context("could not initialize the new Norgolith site"));
           }
       }
       Commands::Serve { port } => {
           // If not using the default port and the requested port is busy
           if *port != 3030 && !net::is_port_available(*port) {
               return Err(anyhow!("The requested port ({}) is not available", port)
                   .context("could not initialize the development server"));
           }

           // If the default Norgolith port (3030) is busy
           if !net::is_port_available(*port) {
               return Err(anyhow!(
                   "Failed to open listener, perhaps the port {} is busy?",
                   port
               )
               .context("could not initialize the development server"));
           }

           cmd::serve(*port).await?;
       }
       _ => {
           println!("TBD");
       }
   }

   Ok(())
}

Now, let’s take a look at the AI refactoring for the cli.rs module:

cli.rs refactoring

And let’s also read the changelog and the refactoring tl;dr produced by Gemini:

cli.rs refactoring changelog

Additionally, let’s look at even more refactoring to simplify a few things:

cli.rs simplifying

Benefits of AI Integration

AI is rapidly transforming the software development landscape, acting as a developer’s secret weapon to boost productivity and free up valuable time. Here’s how AI shines in some specific areas:

Unit Testing Powerhouse

Writing unit tests can be a time-consuming chore. AI can analyze your code and generate comprehensive unit test cases, automating a significant chunk of the process. Imagine the hours saved! This translates to:

  • Increased productivity: you can churn out more features and functionalities without getting bogged down in test writing.
  • Time saved for deeper testing: with AI handling the basics, you can dedicate saved time to crafting more intricate integration and system-level tests, ensuring a robust overall system.

Code Refactoring Robin

AI can be your sidekick Robin for refactoring that obscure Gotham code, Arkham Knight! By analyzing code structure and identifying areas for improvement, AI tools can suggest optimizations and cleaner code. This translates to:

  • Improved code quality: AI helps maintain clean, well-structured code, reducing bugs and making your codebase easier to understand and maintain.
  • Focus on creative solutions: with AI handling the refactoring heavy lifting, you can focus on tackling more innovative coding challenges and architectural improvements.

Documentation Dynamo

Keeping documentation up-to-date can feel like a never-ending battle, even leading to some developers to completely ignore such important task. AI can step in and generate documentation based on your code, providing a solid foundation to build upon and thus making your life easier. This translates to:

  • Reduced documentation overhead: no more spending hours writing comments and explanations. AI gets you started, freeing you to focus on the details and project-specific nuances.
  • More time for creative coding: with AI generating drafts, you can dedicate saved time to crafting clear and concise documentation that effectively communicates the code’s purpose and functionality.

Conclusion

By now, you’ve hopefully seen how AI can be a powerful ally in your development journey. Integrating AI tools for tasks like unit testing, code refactoring and documentation generation can free up significant time, allowing you to focus on the creative aspects of coding you truly enjoy. Remember, AI isn’t here to replace developers, it’s here to empower them.

So, take the first step towards a more efficient and productive development experience. Explore the resources mentioned throughout this post, experiment with different AI tools, and see how they can supercharge your workflow. The future of development is collaborative, and with AI as your partner, you’re well-positioned to build amazing things!