Introduction
The role of developers is changing rapidly. Those who aren’t ready for this AI-powered future risk becoming extinct, like the Dodo 🦤. As Large Language Models (LLMs) improve in their ability to write code, with lower costs and larger context windows, we’re nearing a shift towards Generative AI-driven programming. In this new approach, developers may move away from directly writing code and focus more on being Prompt Engineers and Code Reviewers.
In this article, we’ll explore some key Generative AI Prompt Patterns that are shaping the future of software engineering. These patterns represent innovative techniques designed to optimize the interaction between developers and LLMs, streamlining the development process and enhancing code quality.
Integrating Generative AI into Development Workflows
Before diving into specific Generative AI patterns, it’s crucial to understand how to integrate these powerful tools into your development process effectively. While not all patterns require extended context windows, some advanced techniques significantly benefit from this capability. Moreover, for AI to be truly effective in coding tasks, the chosen models must excel at code generation and understanding.
For robust and scalable AI integration, I strongly recommend using a native, managed cloud service like AWS Bedrock. Such services offer superior reliability, built-in scalability, and simplified management, allowing developers to focus on leveraging AI capabilities rather than maintaining infrastructure. Here’s an example of how to integrate a high-performing LLM, such as Claude 3.5 Sonnet via AWS Bedrock, into your workflow:
import boto3
import json
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=4, max=10))
def call_claude(prompt, max_tokens=1000):
bedrock = boto3.client('bedrock-runtime')
body = json.dumps({
"prompt": prompt,
"max_tokens_to_sample": max_tokens,
"temperature": 0.0
})
response = bedrock.invoke_model(
body=body,
modelId="anthropic.claude-3-5-sonnet-20240620-v1:0",
contentType="application/json"
)
return json.loads(response['body'].read())['completion']
# Usage example for code generation
code_generation_prompt = """
...
"""
generated_code = call_claude(code_generation_prompt)
print(generated_code)
Code language: PHP (php)
This implementation showcases best practices for integrating AI into your development workflow. The @retry decorator implements exponential backoff, enhancing resilience against transient errors. This approach is crucial when working with external AI services, ensuring robust performance even in the face of temporary network issues or service interruptions.
By leveraging AWS Bedrock, you benefit from a managed service that handles the complexities of scaling, security, and maintenance. This allows you to focus on implementing and refining your AI-driven development patterns without worrying about the underlying infrastructure.
As you develop, it’s vital to understand the strengths of the LLM you’re working with and how they align with your project’s needs. The combination of cutting-edge AI models and the reliability of cloud services like Bedrock gives you the perfect foundation for building advanced AI-driven solutions into your software, without the typical hassle.
1. Full-Context Code Analysis Pattern
Leveraging comprehensive codebase understanding for precise AI assistance
This pattern involves inserting the entire codebase or a complete microservice into the prompt, providing the LLM with a comprehensive view of the application. This approach enables more accurate and context-aware responses.
One particularly powerful use of this pattern is for microservices under 1,000 lines of code. By inserting the entire codebase or a complete microservice into the prompt, you provide the LLM with a full view of the application, leading to more accurate, context-aware responses. This is a game-changer for small, self-contained services where a holistic understanding is essential for generating precise and useful output.
Input:
# [Entire microservice code is pasted here]
Please implement a new GET API endpoint in this microservice with the following requirements:
1. The endpoint should return a list of book objects. Each book object should contain:
– id (UUID)
– title (string)
– author (string)
– publication_year (integer)
– genre (string)
2. Implement pagination for this endpoint:
– Accept ‘page’ and ‘size’ query parameters
– Return appropriate metadata (total items, total pages, current page)
3. Apply security measures similar to existing methods in the microservice
4. Implement execution time measurement for this method and log it
Output: The LLM provides a complete implementation of the new endpoint, fully integrated with the existing codebase, including pagination, security measures, and execution time logging.Note: This pattern is often used in conjunction with the Context Reset Pattern, which involves pasting the entire current source code into the LLM chat and asking for confirmation before proceeding with specific queries. This ensures the LLM always has the most up-to-date version of the code.
2. LLM Method Replacement Pattern
Simplifying complex code with AI-generated solutions
This pattern involves replacing complex methods in software applications with LLM-driven solutions. It’s particularly valuable for tasks that benefit from natural language processing capabilities and adaptability to changing requirements.
By leveraging LLMs, you can drastically simplify the readability and maintainability of the methods, making the codebase easier to understand and modify. To ensure reliability, it’s recommended to validate both inputs and outputs, guaranteeing that the LLM’s generated solutions meet the expected standards and integrate seamlessly into your application.
Input:
def extract_financial_data(text):
prompt = f"""
Extract the following financial data from the given text:
- Revenue
- Net Income
- Earnings Per Share (EPS)
- Debt-to-Equity Ratio
Text: {text}
Return the results as a JSON object with the extracted values.
If a value is not found, use null.
"""
try:
response = call_llm(prompt)
financial_data = json.loads(response)
# Validate and process the extracted data
for key in ['Revenue', 'Net Income', 'EPS', 'Debt-to-Equity Ratio']:
if key not in financial_data:
financial_data[key] = None
return financial_data
except Exception as e:
print(f"Error in LLM processing: {e}")
return None
# Usage
text = """
In the fiscal year 2023, XYZ Corp reported strong financial performance.
The company's revenue reached $10.5 billion, up 15% year-over-year.
Net income increased to $2.1 billion, resulting in an earnings per share
(EPS) of $4.20. The company maintained a healthy balance sheet with
a debt-to-equity ratio of 0.8.
"""
result = extract_financial_data(text)
print(json.dumps(result, indent=2))
Code language: PHP (php)
Output:
{
"Revenue": "$10.5 billion",
"Net Income": "$2.1 billion",
"EPS": "$4.20",
"Debt-to-Equity Ratio": "0.8"
}
Code language: JSON / JSON with Comments (json)
3. Context Reducer Pattern
Optimizing structured data generation for efficiency and cost-effectiveness
This pattern addresses the challenges of generating structured data with Generative AI, particularly when dealing with context limitations and output costs. It involves creating a condensed version of the data structure and expanding it in post-processing.
Input:
unstructured_data = """
John Doe, johndoe@email.com, 30 years old, residing in Germany, contact:
+49 123 456789
Jane Smith, janesmith@email.com, 28 years old, residing in France, contact:
+33 987 654321
Mike Johnson, mikej@email.com, 35 years old, residing in Spain, contact:
+34 567 890123
"""
prompt = f"""
Convert the following unstructured user data into a reduced JSON structure
using these abbreviations:
n: name
e: email
a: age
c: country
p: phone
Unstructured data:
{unstructured_data}
Generate the reduced JSON output for all users.
"""
# Generate the reduced JSON structure
response = call_llm(prompt)
def expand_keys(data):
key_mapping = {"n": "name", "e": "email", "a": "age", "c": "country",
"p": "phone"}
return [{key_mapping.get(k, k): v for k, v in item.items()}
for item in data]
reduced_data = json.loads(response)
expanded_data = expand_keys(reduced_data)
print("\nExpanded data:")
print(json.dumps(expanded_data, indent=2))
Code language: PHP (php)
Output:
[
{
"name": "John Doe",
"email": "johndoe@email.com",
"age": 30,
"country": "Germany",
"phone": "+49 123 456789"
},
{
"name": "Jane Smith",
"email": "janesmith@email.com",
"age": 28,
"country": "France",
"phone": "+33 987 654321"
},
{
"name": "Mike Johnson",
"email": "mikej@email.com",
"age": 35,
"country": "Spain",
"phone": "+34 567 890123"
}
]
Code language: JSON / JSON with Comments (json)
4. Comments Replacement Pattern
Enhancing code readability in multilingual development environments
This pattern addresses the challenge of working with codebases containing comments in various languages. It leverages LLMs to translate and adapt comments to a desired language, improving code readability and maintainability.
When using LLMs for this task, generating a patch rather than directly rewriting the entire codebase helps minimize unnecessary changes. This reduces the output to only what’s needed, keeping the process efficient. It’s recommended to thoroughly review the patch to ensure that no unintended code modifications are made in the process.
Input:
You are an expert polyglot programmer.
Your task is to translate all comments in the following Python code
from Japanese to English.
Maintain the original comment structure and enhance clarity where possible.
Generate a Git patch format output for the changes.
def calculate_total(items):
# アイテムの合計金額を計算する
total = 0
for item in items:
# 税込み価格を計算
price_with_tax = item.price * 1.1
total += price_with_tax
# 合計を返す
return total
# メイン処理
if __name__ == "__main__":
# テストデータ
test_items = [Item(100), Item(200), Item(300)]
result = calculate_total(test_items)
print(f"合計金額: {result}円")
Code language: PHP (php)
Output:
diff --git a/sample.py b/sample.py
index abcdef1..ghijklm 100644
--- a/sample.py
+++ b/sample.py
@@ -1,13 +1,13 @@
def calculate_total(items):
- # アイテムの合計金額を計算する
+ # Calculate the total amount for all items
total = 0
for item in items:
- # 税込み価格を計算
+ # Calculate price including tax
price_with_tax = item.price * 1.1
total += price_with_tax
- # 合計を返す
+ # Return the total
return total
-# メイン処理
+# Main process
if __name__ == "__main__":
- # テストデータ
+ # Test data
test_items = [Item(100), Item(200), Item(300)]
result = calculate_total(test_items)
- print(f"合計金額: {result}円")
+ print(f"Total amount: {result} yeùn")
Code language: PHP (php)
Conclusion
The patterns we’ve explored represent just the tip of the iceberg in the rapidly evolving field of AI-assisted software development. As we continue to refine these techniques and develop new ones, we’re witnessing a fundamental shift in how software is created and maintained.
At Capgemini, we’re committed to staying at the forefront of this AI revolution in software engineering. We believe that by embracing these patterns and continually innovating, we can create more efficient, robust, and adaptable software solutions for our clients.
As we move forward, it’s crucial to remember that while AI is a powerful tool, it doesn’t replace human creativity and judgment. The most effective approach is a synergy between human expertise and AI capabilities. By mastering these prompt patterns and remaining open to emerging techniques, developers can position themselves at the cutting edge of this exciting new frontier in technology.
The future of software engineering is one of human-AI collaboration, where our creativity and expertise are amplified by the capabilities of artificial intelligence. Are you ready to be part of this revolution?
Davide Consonni
Head of Cloud Native @Capgemini
Renowned for his expertise in cloud computing and project management, Davide is dedicated to helping organizations leverage cloud-native architectures. His insights will reshape your understanding of digital transformation and enhance your approach to cloud adoption.