Skip to content
GitHub Universe is back: Get tickets now for 35% off, only until July 8

Harness the power of generative AI for software development

Tools like GitHub Copilot and ChatGPT can change the way you build software.

Artwork: Tim Peacock

Photo of Anton Mirhorodchenko
 logo

Anton Mirhorodchenko // Software Developer,

The ReadME Project amplifies the voices of the open source community: the maintainers, developers, and teams whose contributions move the world forward every day.

Also in Developer Stories

Realizing potential with AI

Across industries, large language models (LLMs), such as the ones powering tools like ChatGPT and GitHub Copilot, are automating tasks and revolutionizing the way we interact with both technology and one another. But despite their growing prominence, LLMs are frequently misunderstood or misused, leading to unrealistic expectations—and a certain disenchantment when they’re not met. With the necessary knowledge, however, anyone can effectively harness the power of LLMs to enhance their workflows.

For me, LLMs are about simplifying complex physical actions that are difficult for me to perform, and enhancing skills that I haven’t developed, due to my disability. As someone living with cerebral palsy, typing and speaking are difficult, which limits how much I can write or communicate with people. Without the help of AI, I would have only written to a couple of friends about my project exploring AI-assisted code creation with a brief and modest “Look what I did!” At the same time, I also find it difficult to “showcase” myself, and working with AI helped me by highlighting alternatives. 

This isn’t a characteristic often mentioned regarding LLMs, but I’ve found that they can counter my pessimism with optimistic alternatives, helping me bypass mental challenges. Instead of hiding behind the difficulties of writing and self-doubt, I used AI to write longer, more detailed descriptions, open sourced the project on GitHub to share my findings with other developers, and had it highlighted right here on The ReadME Project a couple months later! Whether it’s expanding abbreviations, summarizing text, helping with translation and writing, or simply offering alternatives, AI has been a powerful force in helping me go farther.

In this article, I aim to demystify LLMs, offer you tips and tricks for integrating them into your own processes, and share the insights I gained from using ChatGPT and GitHub Copilot to aid in the development process. My ultimate goal is to provide you with the tools to significantly improve productivity and efficiency by incorporating LLMs into your workflow, just as I have done. 


In this Guide, you will learn:

  1. How LLMs might work differently than you think and why this matters.

  2. Tips, tricks, and caveats for effective prompt engineering.

  3. Methods for harnessing LLMs in software development.


LLMs: Approximations of intelligence, nothing more

An LLM, like any other neural network, should be regarded primarily as complex mathematical objects. Although there is a strong association between AI and the human brain, it is a misconception. Neural networks only mimic a small fraction of the brain’s processes—enough to achieve notable practical results and successfully approximate real intelligence—but not more. In the same vein, Newton’s laws served for centuries as a very good approximation of real physics processes, but not more. In essence, LLMs provide a reasonable approximation of human language and knowledge, but their approximations aren’t always optimal and they can still make significant errors.

As an end-user, you need to understand that LLMs aim to accurately predict the correct output  by taking into account the context and meaning of the input. As the input’s length increases, it becomes more challenging to find connections between words, leading to a decline in accuracy. If there are too few connections between words they may be entirely overlooked, while too many connections can dilute their significance, which means optimizing your query is essential for effective prompt engineering.

I often have various tasks to complete, but as a non-native English speaker, I prefer to write in my mother tongue. The LLM could easily understand and respond to me in the same language, but this can lead to numerous challenges. LLMs generate their output as a limited sequence of tokens, with some common words represented by single tokens, but with most words represented by several. This is further complicated when the LLM needs to produce non-English language text. In that case, they may need to produce Unicode characters one at a time, which is extremely challenging and leads to slower and less accurate predictions. This means that optimizing my query to generate the proper LLM response format is especially important.

One solution to this problem is a simple tweak in my queries that improves the accuracy and speed of LLM responses. At the very start of the conversation, I just instruct LLM to provide responses in English, regardless of the language (or even multiple languages) that I use in my prompts. By “asking” the LLM to answer in English, we skip that extra complexity in the generation process. (I write “ask” in quotes because LLMs aren’t sentient—they simply detect an “asking” pattern and react in a learned fashion.) For example, I often write my requests in Ukrainian, but ask the LLM, whether ChatGPT or GitHub Copilot, to answer me in English to squeeze out the maximum speed and accuracy. Win-win situation, isn’t it?

Human: Хто такий Леонардо да Вінчі? відповідай на англ. (Translation: “Who is Leonardo da Vinci? Answer in English.”)

GitHub Copilot: Leonardo da Vinci was an Italian polymath of the Renaissance whose areas of interest included invention, painting, sculpting, architecture, science, music, mathematics, engineering, literature, anatomy, geology, astronomy, botany, writing, history, and cartography.

ChatGPT: Leonardo da Vinci was an Italian polymath, born in 1452, who is widely considered to be one of the most influential figures in the history of art and science. He is best known for his paintings, including the Mona Lisa and The Last Supper, but he was also a skilled sculptor, architect, musician, engineer, inventor, and anatomist. Da Vinci’s contributions to fields such as engineering, anatomy, and mathematics were groundbreaking for his time and continue to influence modern science today. His work is considered the epitome of the Italian Renaissance and has had a lasting impact on Western art and culture.

As you can see, the LLMs answered my question in English, despite the fact that I wrote it in Ukrainian, which leads to faster and more accurate responses. 

We want to hear from you! Join us on GitHub Discussions.

Tips and tricks for effective LLM use

During my time exploring AI-assisted coding (which we’ll get to soon), I learned many things about effectively using AI. Below are the top tips and tricks to keep in mind when using an LLM:

  1. Shorten requests: Minimizing text inputs and using shorthand, such as only using just the first letter of a word and the vowel, lets you significantly reduce the amount of typing required, which can make communication faster, more efficient, and accessible. This may also have a positive effect on answer quality, because it reduces the number of tokens used.

  2. Use domain-specific and specialized terminology: Use concise and precise language to convey complex ideas. This helps LLMs recognize patterns and communicate more efficiently, and is especially helpful in technical fields such as science, medicine, and programming.

  3. Avoid formality: Remember, as we explained above, that every word in your input prompt counts and increases both the complexity of the task and potentially the inaccuracy of the response. Keeping your prompt brief can mean removing language you would use to communicate with a person that only adds confusion for an LLM. For example, skip the “Can you…” at the beginning of the sentence and get straight to the query. Every word counts, and an LLM is less concerned with proper grammar than the relationship between the words you use.

  4. Use the “Act as” trick: Prompt LLMs to take on a specific role by using “role snippets” in a conversation to help generate more accurate and complete output. Limit the description to a specific job title, profession, etc. and include instructions or guidelines for what the LLM should or shouldn’t do. This can shape the conversation and achieve more specific and relevant output. Unfortunately, this trick is less effective than it used to be, due to security and safety measures put in place to prevent the LLMs from causing harm or generating inappropriate content. Still, it can set the context for an entire conversation and help shape output.

  5. Use “As a” prompts: Achieve granular control of LLM output by including the direction to “Start with ‘As a [role name], [optional action]’” in your prompts. This method directs the LLM to format its response in a certain way, and allows us to shape the response to achieve more specific and relevant output, while taking much less effort than typical “role snippets.” As we’ll see below, “Act as” is better for assigning a role for the duration of a longer dialogue, while “As a” is better when you want to assign a role for a single reply.

  6. Beware the curse of wording: As mentioned, even small differences in language can have a big impact on results. For example, you might assume that both “fix the bug” and “investigate the bug” have the similar meanings, but each has different implications. “Fix the bug” may imply a straightforward resolution, whereas “investigate the bug” may imply a more thorough investigation into the root cause of the issue before taking action. As such, you need to carefully choose every word.

  7. Use dialogues efficiently: Keep the conversation focused on a single task as much as possible, and don’t hesitate to create as many dialogues as you need. Just like conversing with humans, you shouldn’t mix different topics, tasks, or levels of responsibility (such as junior and senior developer roles) in a single dialogue, because it can become confusing. 

For example, if you want to ask an LLM to fix a bug, don’t ask it to do it in the same dialogue as where you ask it to investigate the bug. Here, we can combine some of the tips described above and instead use a dialogue where you’ve assigned a “senior developer” role to investigate the bug and provide a list of tasks for junior developers. Then, in a separate dialogue where you’ve assigned the LLM a “junior developer” role, you can input that list, requesting the actions the junior developer would take to fix the bug. 

You might be tempted to treat each of these dialogues as a continued conversation, but remember that more context can actually mean more complexity and therefore inaccuracy. Instead, keep just the initial prompt assigning the role and then edit your subsequent prompt with the input to keep the context small and focused. This allows you to work on the next task without polluting the context of a previous, and potentially very different, task. 

Lastly, interrupt response generation to “load” the LLM with context. For example, provide a project description, and then immediately interrupt the response generation so it doesn’t complete. Then, enter the task as the next input. By doing this, you provide the LLM with context as a separate message, but you don’t allow the LLM to “pollute” its own context with its own potentially incorrect response.

Tricks 4 and 5 share similar core ideas, so let's examine them more closely. First, let's compare their prompts.

The “Act as” Prompt:

Human: I want you to act as an IT Architect. I will provide some details about the functionality of an application or other digital product, and it will be your job to come up with ways to integrate it into the IT landscape. This could involve analyzing business requirements, performing a gap analysis and mapping the functionality of the new system to the existing IT landscape. Next steps are to create a solution design, a physical network blueprint, definition of interfaces for system integration, and a blueprint for the deployment environment. My first request is “I need help to integrate a CMS system.”

The “As a” Prompt: 

Human: I need help to integrate a CMS system. Start with, “As an IT Architect, I”

As you can see, the first prompt using a “role snippet” is not only significantly longer than the second, it is also highly dependent on the specific task at hand, which means that you may need to invest additional effort to modify the role specifications for new tasks.

At the same time, AI responds almost identically to them.

The “Act as” Response:

ChatGPT: Sure, I can help you integrate a CMS system into your IT landscape. Before we start, could you provide me with some more information about your business requirements and your existing IT landscape? This will help me to understand your needs and identify any potential gaps or challenges that may arise during the integration process. 

Here are some questions that will help me to better understand your needs:

1. What is the main purpose of the CMS system? Is it for content creation, management, or both?

2. What type of content will you be managing with the CMS? For example, will it be text-based, multimedia-based, or a combination of both?

3. What is your existing IT landscape? Are there any existing content management systems or databases that the CMS system will need to integrate with?

4. What are your scalability and availability requirements for the CMS system? How many users do you expect to be using the system, and how much traffic do you expect to be generated?

5. What security requirements do you have for the CMS system? For example, do you need to comply with any industry-specific regulations or standards?

Once we have a clear understanding of your requirements and existing IT landscape, we can move on to creating a solution design, a physical network blueprint, definition of interfaces for system integration, and a blueprint for the deployment environment. 

The “As a” Response:

ChatGPT: As an IT Architect, I would suggest a step-by-step approach for integrating a CMS system:

1. Identify the CMS system that meets the organization’s needs and requirements. This may involve researching and evaluating different CMS systems based on factors such as features, scalability, security, and compatibility with existing systems.

2. Develop an integration plan that outlines the process and timeline for integrating the CMS system into the organization’s infrastructure. This plan should include details on the technical requirements, resources needed, and any potential risks or challenges.

3. Ensure that the CMS system is compatible with the organization’s existing hardware and software infrastructure. This may involve conducting a compatibility test or making necessary upgrades to the infrastructure. 

4. Set up the CMS system according to the organization’s needs and requirements. This may involve configuring the system’s settings, templates, and plugins to match the organization’s branding and content needs. 

5. Migrate existing content to the new CMS system. This may involve transferring data from the old system to the new system, ensuring that all content is properly formatted and organized. 

6. Train relevant personnel on how to use the CMS system. This may involve providing training sessions or creating documentation on how to use the system effectively. 

7. Monitor the CMS system’s performance and security. This may involve conducting regular security audits and performance tests to ensure that the system is running smoothly and securely. 

By following these steps, IT Architects can ensure a successful integration of a CMS system into the organization’s infrastructure.

How are you working LLMs into your developer workflow? Join The ReadME Project and the author over in GitHub Discussions to let us know!

If we continue the dialogue, the conversation using "role snippets" will keep playing the specified role. By contrast, the conversation using “As a…” often loses the initial instructions and context, and reverts to a normal working mode. This has its advantages, as the LLM is less restricted in choosing its responses, so you can direct the AI's workflow for a single answer and then return to normal mode.

AI-assisted coding: lessons learned

As I’ve mentioned, I recently conducted research on the effective use of AI for creating quality code, which I published in an open source project called User Country Analytics. Instead of focusing solely on the ability of AI to solve problems, I wanted to determine AI’s ability to use best practices, including testing and other aspects. My specific goal in the project was to see if AI could help me use Laravel and Redux effectively, as I had limited prior knowledge of either technology. I had already worked with ChatGPT for some time, so I meticulously studied the AI’s working patterns as I went, so I could share my insights.

Just like in real life, I started the project by consulting with an expert: a software architect. In this case, however, the expert was not human, but rather ChatGPT playing the role of software architect. To start, I asked ChatGPT to break down the overarching task into subtasks and then evaluate their complexity. 

Human: Act like a Software Architect expert. Decompose tasks into simpler ones, evaluate their complexity (0 - an easy task, 50 - a difficult task, from 50 to 100 - consists of subtasks of more than 25 complexity. The maximum complexity is 99) and risks. Provide brief descriptions of tasks WITHOUT/EXCLUDING code examples. Tasks must have a unique number. If you need to repeat some task description, then use a unique number.

You can read the logs of my entire conversation to see that the complexity assessment was not perfect and was more preliminary in nature, but in the end it gave me a list of tasks that needed to be addressed. In fact, after offering a preliminary description of the project, the LLM offered this initial breakdown of the required steps:

ChatGPT:

Task 1: Set up the development environment (complexity: 20, risk: low)

This task involves installing and configuring the necessary software and dependencies, such as PHP, Composer, Node.js, and the Laravel and React frameworks.

Task 2: Create the database and tables (complexity: 10, risk: low)

This task involves creating the MySQL database and the two tables for storing user and country data.

Task 3: Develop the backend API (complexity: 30, risk: medium)

This task involves creating the necessary routes, controllers, and models for handling CRUD operations for the user and country data. It also involves implementing the date filter functionality on the user data.

Task 4: Develop the frontend with React (complexity: 40, risk: medium)

This task involves creating the necessary components for displaying the pie chart, the date filter inputs, and the user data grid. It also involves implementing the functionality for displaying user details in a popup window and handling CRUD operations for the user data.

Task 5: Integrate the backend and frontend (complexity: 20, risk: low)

This task involves connecting the frontend to the backend API and ensuring that data is properly displayed and updated on the frontend.

Task 6: Test and debug (complexity: 20, risk: low)

This task involves thoroughly testing the application for any bugs or edge cases and debugging any issues that are found.

Task 7: Deploy the application (complexity: 10, risk: low)

This task involves deploying the finished application to a publicly available repository.

From there, I took additional steps to get more specific details about complexity, what needed to be done, and how:

  1. Provide ChatGPT with a project description and request an expert’s evaluation of subtask complexity on a scale of 1 to 100.

  2. Ask ChatGPT to break down the five most complex tasks into subtasks.

  3. Repeat the second step until all subtasks have a complexity below 30, which is the level of complexity I would expect a junior developer to handle.

  4. Request a list of tasks “for a junior developer” accompanied by their descriptions and the execution conditions. This second part is key, as it provides a more granular breakdown of tasks and increases the effectiveness of dialogues/roles with the AI.

ChatGPT has an extensive knowledge base and can solve many tasks, including writing code. However, it is highly inefficient in this realm. Firstly, if you’re using the free version, you can quickly exhaust the message limit. Secondly, ChatGPT’s context can easily become polluted, leading to degraded usefulness of its responses. Thirdly, you cannot control its code quality.

In contrast, GitHub Copilot specializes in writing code. It understands comments and task descriptions well, and allows you to effectively influence individual code nuances. For example, you can quickly change a variable name and generate code that will work with the new name.

With this in mind, I used GitHub Copilot for code generation and ChatGPT for higher-level operations, such as task description, breaking down tasks into subtasks, complexity estimation, and so on. My approach involved a back-and-forth between the two LLMs:

  1. First, I assigned ChatGPT the role of a programming teacher/tutor and asked it to write a code template for the task—not the main code, only the general structure and comments for where the code will need to be inserted.

  2. Next, I copied the code template provided by ChatGPT into VS Code and used GitHub Copilot to generate the specific code, editing it as necessary.

This simple approach was surprisingly effective. I was shocked when GitHub Copilot generated over a hundred lines of code, including comments and variable descriptions, in a matter of secon

ds. Of course, it wasn’t very complex code—just a typical unit test—but it saved me several hours of work and, more importantly, mental effort. While I had to make some adjustments, it was significantly easier than writing the code from scratch.

During the initial stages of project creation, you can also use a slightly different version of my previously outlined approach:

  1. Give ChatGPT a list of problems, ask it to create a list of files needed to solve them, and then associate each file with a description of the problem it should solve.

  2. Ask for a bash or batch script, or even a single command line input, that creates all the necessary files and inserts a comment with the problem description into each file. 

  3. Finally, use GitHub Copilot on each file to generate code based on the included comment.

 While I didn’t delve too deeply into this method, as it is only useful at the very beginning of the project, it has also proven quite effective. I used this method during the initial stages of my project, and you can see in my examples how ChatGPT provided the individual steps to set up my environment, even correcting previous responses when I entered error messages. In general, ChatGPT is good at using bash and batch scripts, so many routine tasks can be simplified that way. However, don’t blindly rely on ChatGPT. You should always check that the code it generates isn’t harmful. 

My last, and possibly most important, piece of advice is to always make sure that ChatGPT is using the same versions of the language, libraries, and frameworks as you are. In this project, I encountered many problems simply

because the API had changed, and ChatGPT was using an old version because it is not continuously trained and kept up to date. The differences in the API weren’t obvious, and I didn’t immediately understand the cause of the problem, as the generated code looked perfectly correct. Paying attention to this detail can save you a lot of time (and frustration).

Experts still required

Following best practices and using the various tricks I’ve outlined above can make incorporating LLM-based tools into your workflow highly effective. Remember to maintain a clear input context and find the right balance between using domain-specific terminology while avoiding the formalities of and etiquette of normal conversation. Experiment with different approaches, such as using shorthand, specialized terminology, or the “Act as” trick, to help improve output quality.

Cartoon illustration showing two men sitting on a bus, on either side of the aisle. One man is happy and the other man is sad both with the phrase “AI will take my job” above them.

For code-related tasks, use ChatGPT in conjunction with GitHub Copilot to yield impressive results. Use ChatGPT for high-level tasks like breaking down problems into smaller tasks, estimating complexity, and generating code templates. Meanwhile, use GitHub Copilot for generating actual code based on these templates.

We want to hear from you! Join us on GitHub Discussions.

Before you get too excited (or scared) about AI’s potential, it’s important to note that AI cannot fully replace human specialists. Instead, you should think of AI as a team member, and you should always carefully review and test AI-generated output to ensure its correctness and safety. Just like any other team member, AI has its strengths and weaknesses. It can process vast amounts of data quickly and accurately, but it still requires direction and guidance from a skilled leader who understands these qualities and delegates tasks accordingly. Just as a team of specialists and generalists can achieve great things by working together, combining the strengths of humans and AI can lead to even greater achievements. AI is a tool to augment your skills, not replace them. 

A software developer from Ukraine, Anton has been passionate about AI for several years, particularly in reinforcement learning, unconventional approaches, and using AI to tackle accessibility issues. Despite having cerebral palsy, and enduring the challenges of housing insecurity and war in his native country, Anton stays positive and finds ways to overcome obstacles and pursue his passions. He is excited to share his experiences and ideas with this community and learn from others as well.

About The
ReadME Project

Coding is usually seen as a solitary activity, but it’s actually the world’s largest community effort led by open source maintainers, contributors, and teams. These unsung heroes put in long hours to build software, fix issues, field questions, and manage communities.

The ReadME Project is part of GitHub’s ongoing effort to amplify the voices of the developer community. It’s an evolving space to engage with the community and explore the stories, challenges, technology, and culture that surround the world of open source.

Follow us:

Nominate a developer

Nominate inspiring developers and projects you think we should feature in The ReadME Project.

Support the community

Recognize developers working behind the scenes and help open source projects get the resources they need.

Thank you! for subscribing