The Software Engineer as Tool-User

James Davis
9 min readAug 14, 2023
This essay explores the ways in which software engineers use tools, focused on themes of Efficiency and Responsibility.

Engineering Tools

Engineers develop and use tools to make our work more efficient.

  • Digging dirt with a stick is difficult — a shovel makes things go more quickly. If a large amount of dirt must be moved quickly, a more advanced tool can be employed, such as an excavator.
  • Similarly, joining together two pieces of wood is difficult without tools. If a carpenter wants to do this more quickly, they may use a hammer and nails, or clamps and glue.

As we use tools, we must not give up our responsibility as engineers. There is an old saying that “Only bad craftspeople blame their tools”. Tools can make engineers more efficient, but tools can also introduce defects into engineered products. For example, if you are trying to join together two pieces of wood, you might glue them together. Yet suppose you are in Antarctica: the glue may fail in extreme cold.

An engineer must understand the context in which the product will be used in order to decide which tools to use. Tools must be used properly; the engineer is responsible for the product regardless of the chosen tools.

Tools thus benefit engineers, but they also require engineers to take responsibility for using them properly. The Software Engineering Code of Ethics was published jointly by the two major professional societies for computing, the Association of Computing Machinery (ACM) and the Institute of Electrical and Electronics Engineers (IEEE). This code is focused on software products and software engineers. It makes no mention of the tools used to build a product. The responsibility for a high-quality product lies with the software engineering team that produce the product. The software engineers must select and use their tools skillfully, keeping costs low (better efficiency) while promoting high quality of the product (responsibility).

Using Tools in Software Engineering, Efficiently and Responsibly

Software engineers are tool-users. This interpretation carries with it the characteristics of efficiency and responsibility. We illustrate this viewpoint by examining some tools that might be used while performing different software engineering tasks.

When Writing Software

As you completed a programming task, you may refer to external resources to better understand the task or the errors you encounter. I think of these external resources as knowledge tools.

  • For example, you might refer to the Linux manual for bash or for some of the APIs you used, such as library calls (e.g., malloc and free) or system calls (e.g., open, read, write, and close).
  • Alternatively, you might have sought a resource on the Internet and found a pertinent Question and Answer on a discussion forum such as Stack Overflow.

These tools have different characteristics for efficiency and responsibility.

Reading a manual is slow going. On my MacBook, the programming manuals for open, read, write, and close total 3,548 words, or a little over 7 single-spaced pages. I suppose it would take over 10 minutes to read all of them, even if they are simple and easy to digest (they are not!).

Meanwhile, I find that many of my questions about reading and writing files can be answered in about 20 seconds on Stack Overflow. Clearly, Stack Overflow provides large efficiency gains.

Let us now consider responsibility. I have seen students complete entire programming assignments by dint of repeated recourse to Stack Overflow. They copy code to address each error, and the code compiles. But they end up with a Franken-solution that they don’t fully understand, and so when it doesn’t quite pass all of the test cases they struggle to debug and repair it.

In contrast, students who study the manuals may take longer to finish the assignment, but they will make steady progress instead of having to make many revisions.

As engineers, we want both — we want our products to be efficient, and yet responsibly-made. Tools such as Stack Overflow are valuable because they help us skip right to the part of a manual that is pertinent. I am not advising you to ignore that. But I do advise you to use Stack Overflow and similar knowledge resources responsibly. When I use Stack Overflow, I ask myself four questions:

  1. “Do I have enough expertise to understand whether the advice is sound?” If I do, splendid. But if not, my time might be better spent developing my expertise.
  2. “Will I be able to debug this code snippet when it breaks?’’ Most Stack Overflow posts need to be tailored to your context, and many have subtle errors because they are intended as illustrations. You should never take code from someone else without knowing how it works.
  3. “How critical is it that I fully understand this problem and its solution?’’ In engineering work, some knowledge is critical and other knowledge is not. Most engineers don’t need a deep mastery of date-time formats or the RFC defining valid email addresses. Internet resources are a great way to find suitable code snippets for such tasks, which you can then bury within a friendly API in a utility library. But if you are working on code for critical business processes (or the key algorithm in your school project), step carefully.
  4. “How long have I been looking for a solution to my problem?’’ Personally, I use a “5 minutes”’ rule. If I have been looking for a solution for more than 5 minutes, this is usually a signal that there’s something wrong with my question. That means I should switch to studying and developing my understanding of the problem and the tools I am using. An alternative explanation is that my problem is sufficiently obscure that I am one of the first people to try to solve it— so I’d better focus on solving it!

When Debugging Software

Most software engineers don’t get the code perfect the first time around. One approach for debugging, or finding the errors in your code, is to carefully read the code and see if you can spot the error. This approach is nearly tool-less, not counting the tool you are using to read the code.

There are some tool-based approaches. Let’s talk about two of them.

  • First, there’ s tools like printf, which enable the strategy called logging: a program is instrumented with printf statements that report its dynamic behavior to the engineer.
  • Another tool is called a debugger, such as gdb (command-line) or ddd (a graphical interface for gdb).

These tools have different characteristics for efficiency and responsibility. It is hard to say whether logging or debugging is a more efficient approach. Depending on the nature of the bug, a single log statement might help you find it; or a long session with a debugger might be necessary to truly understand a defect that lies in the interplay of two components. There is plenty of research that shows, however, that either of these tools will be more efficient than trying to understand an error somewhere in hundreds of lines of code just by reading the code.

This brings us to the matter of responsibility. Here we must tread carefully. When you study software carefully (the tool-less approach), you end up with a deep knowledge of its behaviors and assumptions. After you identify the bug, you will likely be able to repair it without compromising existing behaviors or assumptions.

In contrast, I have observed many students whose use of loggers or debuggers accelerates their discovery of a bug, but who then take shortcuts in repairing it because they have not truly understood the bug in its context.

My point is not that you should never use a logger or a debugger. These tools make you more efficient at their goal — locating the defect. However, you must use them responsibly. Once you have located the defect, you must then study the code to ensure that your repair does not break something else.

When Delegating Software Engineering Work

The computing field is always changing. One major recent change has been the advent of Generative Artificial Intelligence tools (GenAI) such as OpenAI’s ChatGPT tool, Google’s Bard tool, and GitHub’s CoPilot tool. These tools are also called “large language models” (LLMs) because the core of their implementations are LLMs.

Some of these tools are structured as question-answering systems similar to Stack Overflow. Others are more closely integrated with the software engineering process, such as the auto-complete functionality offered by CoPilot.

Both of these styles of GenAI differ from prior software engineering tools in that they are interactive. Previous tools, whether debuggers, code generators, or knowledge collectors, were static and required you to adapt whatever they produced to fit your particular context. These new GenAI tools are dynamic; if you have additional requirements, you can ask them to refine their proposals to your special case.

Since GenAI tools were developed by software companies, they are particularly good at software-related tasks. You can ask them to implement common data structures, explain errors in your code, write documentation, and generate “boilerplate” material such as implementing a web client using a particular library.

Because GenAI tools are interactive, I think of them less as a question-answering tool like Stack Overflow, and more as a kind of engineering assistant to whom I can delegate some of my work.

I strongly encourage you to work with GenAI tools.

The software engineers who learn to use these tools well will find themselves vastly more productive than those who refuse or use them inexpertly. This should come as no surprise, of course, because engineers are tool users and GenAI is just another kind of tool.

But like all tools, using GenAI will require you to balance efficiency against responsibility. Like all tools, you should use GenAI only to the limit of your own competence. If you will not be able to assess the result of the tool, then you should not use the tool.

I have two specific recommendations when working with the current generation of GenAI tools.

  1. You should fully understand the specification of the software you are trying to create. GenAI tools are great at providing a first draft of software, but they will struggle to catch all of the edge cases — and filling in those edge cases via prompts may be less efficient than simply editing the first draft proposed by the tool.
  2. You (the engineer) will have to do the work of design. The current generation of GenAI is great at filling out a template, or implementing individual functions, but it struggles when asked to come up with the template or to decompose a problem into individual functions.

Although you must exercise your engineering judgment, even the current generation of GenAI allows you to access expertise that might otherwise be expensive to acquire.

For example, you might be developing software to run on a particular hardware platform like ARM64.

  • You might know, conceptually, that ARM64 includes optimized instructions such as Single-Instruction-Multiple-Data (SIMD) to apply the same operation to an entire vector of data. This reduces the time to transform the vector, as well as the energy cost of doing so; both latency and energy efficiency are reasons to draw on these capabilities.
  • However, you might not know the specific incantation to employ SIMD in your context, nor the preferred libraries for doing so.

I recently worked with ChatGPT-3.5 to develop a working program tailored to ARM64, and what would have taken days (from reading manuals) or hours (from scouring the Internet) took just minutes instead. GenAI tools gave me a substantial efficiency boost, and because I had the conceptual mastery of SIMD I was able to responsibly assess (and revise) the proposals of ChatGPT.

Engineering expertise and engineering judgment will allow you to ask the right questions of GenAI, to assess the quality of the results, and to revise them as needed. Or, in other words, you need your engineering expertise and your engineering judgment to use GenAI both efficiently and responsibly.

I ultimately view GenAI as an assistant to a software engineer. The engineer remains responsible both for the design as well as the implementation. The GenAI tool makes the creation of the implementation more efficient, but the engineer remains responsible for ensuring that the result is well designed and fully solves the problem at hand.

The software engineer will not be replaced; don’t worry that your job will be automated away. But to perform well in that job, you should use all of the tools at your disposal, and that includes GenAI.

Closing remarks

Computing is an exciting field to enter. Modern society runs on computers, and computers obey their software and hardware. Computer engineering (whether focused in software or hardware) is truly the profession of the 21st century.

One characteristic of computing is that nothing stays the same. Like Moore’s Law, it seems that every 2–3 years, a groundbreaking technology or tool is introduced to improve the productivity of computer engineers. Engineers are responsible for learning these new tools — otherwise you are doing yourself, your employer, and your customers a disservice.

You must always use these tools efficiently and responsibly.

Use your judgment, and have fun.

Postscript: A question for software engineering research

In software engineering research, the standard metrics are focused on efficiency (e.g. fewer lines of code, or time-to-complete a task). Can and should we measure the effect that a tool (or suite of tools) has on engineering responsibility? Or whether some forms of a tool discourage or promote responsibility?

--

--

James Davis

I am a professor in ECE@Purdue. My research assistants and I blog here about research findings and engineering tips.