Programming in C++/Java/Python

Chapter 2
Development Environments and Source Control

Diploma in Software Engineering
Programming in C++/Java/Python   •   Diploma

Development Environments and Source Control

Introduction

A programming language is useful only when a developer can translate source code into an executable program or run it through an interpreter, inspect errors, manage dependencies, and preserve a reliable history of changes. These activities take place within a development environment. A development environment may be as simple as a text editor and command-line compiler, or as integrated as a modern IDE connected to automated builds, testing services, package registries, and collaborative Git repositories.

For software engineering students, learning these tools is not merely a matter of memorising commands. The important skill is understanding how the tools relate to one another. A compiler or interpreter processes source code; an IDE coordinates editing, execution, debugging, and project navigation; a package manager obtains reusable libraries; a build system automates repeatable construction; and source control records how a project changes over time. Together, these components support reproducibility, teamwork, quality assurance, and professional accountability.

This chapter uses C++, Java, and Python as examples. Commands differ between operating systems, but the underlying principles are transferable.

Learning Objectives

By the end of this chapter, you should be able to:

  • distinguish compilers, interpreters, runtimes, IDEs, editors, build systems, and package managers;
  • configure and verify a basic C++, Java, or Python development environment;
  • compile, run, debug, and package a small program from the command line;
  • explain project structure, dependencies, environment variables, and configuration files;
  • create a Git repository and use commits, branches, remotes, merges, and pull requests;
  • apply a basic collaborative workflow and resolve simple merge conflicts;
  • identify security, reliability, licensing, and reproducibility risks in development environments.

1. The Development Toolchain

A toolchain is the collection of programs that transforms source code into a running application and supports its maintenance. A typical workflow is:

Software development toolchain Source code.cpp .java .py Build or runcompiler/interpreter Test and debugchecks and diagnosis Packageartifact or release Source control: history, branches, collaboration
Software development toolchain from source code to deployment
Figure: Software development toolchain from source code to deployment

Figure: A simplified relationship among source code, execution, quality activities, packaging, and source control.

1.1 Compilers, interpreters, and runtimes

A compiler translates source code into another form before execution. A C++ compiler commonly translates source files into object files and then a linker combines object files and libraries into an executable. Java source is compiled into bytecode, normally stored in .class files, and executed by the Java Virtual Machine (JVM). Python is commonly executed by an interpreter implementation such as CPython; it may first produce internal bytecode, but the programmer generally experiences the process as interpreted execution.

The distinction is useful but not absolute. Modern language systems may use bytecode, just-in-time compilation, optimisation, caching, or ahead-of-time compilation. The practical questions are: what command processes the source, what output is produced, what runtime is required, and where are errors reported?

A compiler can report syntax and type errors before a program runs. An interpreter can report errors while loading or executing code. Neither approach guarantees that a program is correct: successful translation means only that the program satisfies certain language and tool requirements.

1.2 Editors and IDEs

A text editor provides facilities such as character input, search, and file management. An integrated development environment (IDE) combines editing with language-aware features, project management, building, debugging, testing, and version-control integration. Examples include Visual Studio, IntelliJ IDEA, Eclipse, and PyCharm; Visual Studio Code is an extensible editor that can provide IDE-like capabilities through extensions.

An IDE does not replace the underlying toolchain. It usually invokes a compiler, interpreter, debugger, or build tool behind the scenes. Understanding command-line execution is therefore essential when an IDE configuration fails, when a program must run on a server, or when a build must be automated.

1.3 Debuggers and diagnostic output

A debugger allows execution to be paused at breakpoints, examined through variables and call stacks, and continued step by step. A breakpoint is a deliberate stopping point. The call stack records active function or method calls. A watch expression evaluates selected values while execution is paused.

Diagnostic output, logging, assertions, and tests complement debugging. Debugging explains a particular execution; tests provide repeatable evidence about expected behaviour. A professional workflow uses both rather than relying on print statements alone.

2. Installing and Verifying a Basic Environment

A reliable installation begins with identifying the operating system, processor architecture, required language version, and project requirements. Prefer official distribution channels and document the versions installed. Avoid placing several incompatible tool versions on the system path without a deliberate reason.

The PATH environment variable is a list of directories searched by the operating system when a command is entered. If a compiler is installed but its directory is absent from PATH, the shell may report that the command cannot be found. A command such as where on Windows or which on Unix-like systems can help identify the executable selected.

Typical verification commands include:

c++ --version
java --version
javac --version
python --version
python -m pip --version

The exact executable may be g++, clang++, python3, or another platform-specific name. The important practice is to verify both availability and identity. A project can appear to work in an IDE while the terminal uses a different version.

2.1 A minimal C++ workflow

Create hello.cpp:

#include <iostream>

int main() {
    std::cout << "Hello, software engineering!\n";
    return 0;
}

Compile and run it with a standards-compliant compiler:

c++ -std=c++17 -Wall -Wextra -pedantic hello.cpp -o hello
./hello

On Windows, the executable may be hello.exe. The -std=c++17 option selects a language standard, while warning options request potentially useful diagnostics. Warnings are not automatically errors, but treating serious warnings as defects is a sound practice.

For a multi-file program, compilation and linking are separate conceptual stages. A source file may be compiled into an object file, and object files are then linked with libraries. A build system records these relationships so that only affected files need to be rebuilt.

2.2 A minimal Java workflow

Create Hello.java:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, software engineering!");
    }
}

The public class name must match the file name. Compile and run:

javac Hello.java
java Hello

javac produces bytecode, and java launches the class through the JVM. Java projects commonly use Maven or Gradle to manage dependencies, compile source, run tests, and create distributable artifacts.

2.3 A minimal Python workflow

Create hello.py:

print("Hello, software engineering!")

Run it with:

python hello.py

For a project, create an isolated virtual environment:

python -m venv .venv

Activate it using the command appropriate to the operating system, then install declared dependencies. The virtual environment prevents project packages from being mixed with the system Python installation.

3. Project Structure, Dependencies, and Build Systems

A project should make its organisation understandable to another developer. A small application might contain the following:

project/
├── src/                 application source
├── tests/               automated tests
├── docs/                user and developer documentation
├── README.md            purpose and usage
├── .gitignore           files excluded from Git
└── configuration file   build or dependency instructions

A dependency is an external library, tool, or runtime required by a project. Dependencies should be declared explicitly, preferably with versions or compatible version ranges. Installing an undeclared package manually creates the “works on my machine” problem: another developer cannot reproduce the environment.

Package managers provide standard mechanisms for obtaining and recording dependencies. Common examples include:

EcosystemTypical package manager or build toolCommon declaration
C++Conan, vcpkg, CMakeCMakeLists.txt, manifest files
JavaMaven, Gradlepom.xml, build.gradle
Pythonpip, Poetry, uvrequirements.txt, pyproject.toml

A package manager is not automatically a guarantee of safety. Packages may contain vulnerabilities, malicious code, incompatible licences, or abandoned components. Use trusted registries, review dependency information, update deliberately, and scan where appropriate.

3.1 Build systems

A build system describes how source files become outputs. It can compile code, run tests, generate documentation, package artifacts, and clean temporary files. Build automation improves consistency because the same instructions can be executed locally and in continuous integration.

A build should be deterministic as far as practical: given the same source, declared dependencies, configuration, and tool versions, it should produce equivalent results. Exact reproducibility can be difficult because operating systems, compilers, timestamps, and external services vary. Lock files, container images, pinned tools, and documented procedures reduce variation.

CMake is often used to generate native build files for C++ projects. Maven follows conventional Java project phases such as compilation, testing, and packaging. Python projects increasingly use pyproject.toml to describe build metadata and dependencies. The tool choice matters less than having a documented, repeatable process.

4. Git and Distributed Source Control

Source control records changes to files and enables recovery, comparison, collaboration, and release management. Git is a distributed version-control system: each clone normally contains the repository history, allowing many operations without a network connection.

Important Git concepts are:

  • Working tree: files currently checked out and edited.
  • Index or staging area: proposed contents for the next commit.
  • Commit: a permanent snapshot reference with metadata and a message.
  • Branch: a movable reference to a line of development.
  • Remote: a named reference to another repository, often hosted online.
  • Merge: integration of histories from different lines of development.
  • Tag: a durable name commonly attached to a release commit.

A basic repository workflow is:

git init
git status
git add src/README.md
git commit -m "Add project introduction"
git branch -M main
git remote add origin <repository-address>
git push -u origin main

The command git status should be used frequently. It reveals untracked files, modified files, staged changes, and the current branch. A commit should represent a coherent, reviewable change. Commit messages should explain the change’s purpose rather than merely repeating a file name.

4.1 Ignore files and sensitive information

A .gitignore file excludes generated output, local environments, editor metadata, and secrets. Examples include compiled binaries, Python .venv directories, Java build directories, and temporary files. A secret accidentally committed to Git should be considered exposed even if the file is later deleted: it may remain in history or in another clone. The correct response includes revoking or rotating the credential, assessing exposure, and then cleaning history if necessary.

Never commit passwords, private keys, access tokens, or production configuration. Use environment variables or an approved secret-management service, and provide a safe example configuration such as .env.example without real credentials.

4.2 Branches, remotes, and collaboration

A common collaborative workflow is:

  1. Update the local main branch.
  2. Create a short-lived branch for one feature or defect.
  3. Make small commits and run tests locally.
  4. Push the branch to a shared remote.
  5. Open a pull request for review.
  6. Address feedback and automated checks.
  7. Merge using the team’s agreed policy.
  8. Delete the completed branch when appropriate.

For example:

git switch main
git pull --ff-only
git switch -c fix-input-validation
# edit files and test
git add .
git commit -m "Validate empty user input"
git push -u origin fix-input-validation

The option --ff-only refuses an implicit merge when the local history has diverged, making the situation explicit. Teams may choose merge commits, rebasing, or squash merging. Each policy can work if members understand it and protect the main branch with review and automated checks.

4.3 Merge conflicts

A conflict occurs when Git cannot automatically reconcile competing changes, often because two branches changed overlapping lines. Git marks the affected file with conflict markers. The developer must inspect the intended result, remove the markers, run tests, stage the corrected file, and complete the merge or rebase.

A conflict is not solved merely by choosing one side mechanically. The correct resolution preserves the required behaviour from both changes or deliberately rejects one after discussion. Communication is particularly important when the conflict concerns an interface, database schema, security rule, or public behaviour.

5. A Practical Collaborative Case

Background

This illustrative case concerns a diploma student team building a small appointment-booking application. One member uses Windows and an IDE; another uses Linux and the command line; a third uses macOS and a different Java version.

Problem

The application works on one laptop but fails on another. The team has no dependency declaration, generated files are committed, and two students edit the same main branch. A review discovers that a database password was committed accidentally.

Analysis

The failures have several causes: inconsistent Java versions, undocumented setup steps, unmanaged dependencies, and weak source-control practice. The exposed password is an immediate security incident, not merely a Git mistake.

Approach and solution

The team documents the required Java version and uses Maven with a project descriptor. Dependencies and test commands are recorded in the README. Generated output is removed from version control and added to .gitignore. A protected main branch is introduced. Each task uses a feature branch and pull request, and automated checks compile and test the project.

The password is revoked and replaced. The team removes the credential from active configuration and uses environment variables during development. They explain to the project supervisor that deleting the visible line alone is insufficient because repository history may retain it.

Outcome

A new developer can clone the project, configure the required environment, execute one documented build command, and run the tests. Changes are reviewed before entering main, and failures are visible through automated checks. The workflow takes modest effort initially but reduces repeated troubleshooting.

Learning points

The case demonstrates that environment configuration, build automation, dependency management, and source control are one connected engineering problem. Git cannot compensate for undocumented builds, and an IDE cannot compensate for insecure credential handling. Reproducibility and collaboration are design goals, not accidental benefits.

6. Quality, Security, and Professional Practice

Development tools have authority over source files, build outputs, and sometimes deployment systems. Install only software from reputable sources, keep operating systems and tools updated, and use least privilege. Extensions in an IDE can read project files or execute commands, so they should be reviewed like other software.

Teams should define formatting rules, naming conventions, supported tool versions, test expectations, and review standards. Automated formatters and linters reduce avoidable disagreements. Continuous integration should perform clean builds and tests rather than depending on a developer’s unrecorded local state.

Licensing also matters. A dependency’s licence can impose attribution, notice, source-distribution, or compatibility obligations. Students and professionals should read the licence information for libraries used in deliverables and follow institutional or client policy.

Artificial intelligence coding assistants can suggest code, tests, and explanations, but their output requires human review. Generated code may contain security defects, licensing uncertainty, fabricated APIs, or inappropriate assumptions. Do not submit confidential source code to an AI service without authorisation. Record significant assistance when academic integrity or professional policy requires it.

7. Troubleshooting Method

When a program fails, change one variable at a time and collect evidence. A useful sequence is:

  1. Read the complete error message and identify the command that failed.
  2. Verify the current directory and file names.
  3. Check tool versions and the selected executable.
  4. Reproduce the failure with the smallest command or test.
  5. Inspect environment variables and dependency installation.
  6. Compare the working and failing environments.
  7. Consult official documentation and reliable issue reports.
  8. Record the cause and fix in project documentation.

Common symptoms have different causes. “Command not found” often indicates installation or PATH configuration. “Class not found” may indicate an incorrect Java class path or build output. A Python import error may indicate the wrong virtual environment. A linker error in C++ often means that a declaration was found but its implementation or library was not linked. Diagnosis should follow evidence rather than repeated reinstallations.

Chapter Summary

A development environment is an interacting toolchain rather than a single application. Compilers translate C++ and Java source into executable or intermediate forms, while Python is commonly run through an interpreter implementation. IDEs coordinate tools but do not replace the underlying command line. Package managers and build systems make dependencies and construction procedures repeatable. Git records project history through commits, branches, remotes, and merges, enabling review and collaboration. Effective practice includes version verification, isolated environments, documented builds, meaningful commits, protected main branches, conflict resolution, dependency and licence awareness, and careful treatment of secrets. Security, reproducibility, and professional communication are essential parts of tool use.

Key Terms

Compiler: A program that translates source code into another representation, often machine code or bytecode.

Interpreter: A program that executes source code or an intermediate representation.

Runtime: Software and services required while a program executes.

IDE: An integrated development environment combining editing, building, debugging, and project tools.

Debugger: A tool for pausing execution and inspecting program state.

PATH: An environment variable containing directories searched for executable commands.

Dependency: An external library, tool, or runtime required by a project.

Package manager: Software that obtains, installs, and records reusable packages.

Build system: Tools and instructions that automate compilation, testing, and packaging.

Artifact: A generated output such as an executable, library, archive, or package.

Repository: A directory and history managed by a version-control system.

Working tree: The currently checked-out project files in Git.

Staging area: The set of changes selected for the next Git commit.

Commit: A recorded snapshot of selected project changes.

Branch: A movable reference representing a line of development.

Remote: A named reference to another repository.

Merge conflict: A condition in which changes cannot be integrated automatically.

Continuous integration: Automated building and testing of proposed changes.

Reproducibility: The ability to recreate equivalent results from documented inputs and tools.

Secret: Sensitive information such as a password, token, or private key.

Review Questions

Short Answer Questions

  1. What is the difference between a compiler and an interpreter?
  2. Why should a developer verify tool versions from the command line?
  3. What purpose does the Git staging area serve?
  4. Why are virtual environments useful in Python projects?
  5. What is a merge conflict, and how should it be resolved?

Long Answer Questions

  1. Explain how an IDE, compiler or interpreter, debugger, package manager, and build system cooperate during software development.
  2. Describe a professional Git workflow from creating a feature branch through code review and integration.
  3. Compare dependency management in C++, Java, and Python, including the role of project declaration files.
  4. Explain how a team can improve the reproducibility of a project across different operating systems.
  5. Discuss the security, licensing, and academic-integrity risks associated with third-party packages and AI coding assistants.

Analytical / Application Questions

  1. A Java project runs in one developer’s IDE but fails in continuous integration with a missing dependency. Analyse the likely causes and propose a systematic investigation.
  2. Two developers changed the same validation function on separate branches, producing a Git conflict. Describe how you would determine the correct combined behaviour and verify the resolution.
  3. A student commits an API token, then deletes the file and pushes the deletion. Explain why the token must still be revoked and outline a safer replacement workflow.

Multiple Choice Questions with Answers

Question 1

Which tool normally translates Java source code into JVM bytecode?

A. The Java compiler, javac

B. The Git client

C. The IDE theme manager

D. The package registry

Correct Answer: A. The Java compiler, javac

Question 2

What is the primary purpose of a Python virtual environment?

A. To increase monitor resolution

B. To isolate project packages from the system installation

C. To convert Python into C++

D. To replace source control

Correct Answer: B. To isolate project packages from the system installation

Question 3

Which Git area contains changes selected for the next commit?

A. Remote repository

B. Working tree only

C. Staging area

D. Issue tracker

Correct Answer: C. Staging area

Question 4

What does a build system primarily automate?

A. Physical computer repairs

B. Compilation, testing, and packaging activities

C. Writing all application requirements

D. Replacing every dependency with custom code

Correct Answer: B. Compilation, testing, and packaging activities

Question 5

Which practice is safest after an access token is committed to a repository?

A. Rename the file only

B. Delete the visible line and take no further action

C. Revoke or rotate the token and investigate exposure

D. Make the repository public for transparency

Correct Answer: C. Revoke or rotate the token and investigate exposure

Question 6

What is the usual purpose of a feature branch?

A. To isolate work on a change before integration

B. To remove all project history

C. To store operating-system backups

D. To disable automated tests

Correct Answer: A. To isolate work on a change before integration

Question 7

Which command reports the current Git branch and changed or untracked files?

A. git status

B. git license

C. git compiler

D. git install-os

Correct Answer: A. git status

Question 8

Why should generated binaries commonly be placed in .gitignore?

A. They are always confidential

B. They are reproducible outputs that need not clutter source history

C. Git cannot store text files

D. They prevent all merge conflicts

Correct Answer: B. They are reproducible outputs that need not clutter source history

Question 9

A linker error in a C++ project most directly suggests that the program has failed during which stage?

A. Combining object files and libraries into an executable

B. Selecting a Git branch name

C. Writing a README heading

D. Activating a Python environment

Correct Answer: A. Combining object files and libraries into an executable

Question 10

What is a central benefit of continuous integration?

A. It guarantees that software has no defects

B. It automatically builds and tests proposed changes

C. It eliminates the need for source code

D. It makes all dependencies unnecessary

Correct Answer: B. It automatically builds and tests proposed changes

Programming in C++/Java/Python
Chapter 2 of 15