TeamCity was launched by JetBrains in 2006 as a commercial build management and continuous integration server.
Originally developed as a Java web application, it gained popularity for its deep integration with IDEs (like IntelliJ IDEA) and rich visual pipeline status dashboards.
Key milestones:
First-Class Java/.NET Support: Excelled at handling enterprise compilers and testing frameworks early on.
Build Chains (DAGs): Introduced visual, robust build pipeline dependency trees before many other tools.
Configuration-as-Code (Kotlin DSL): Introduced in TeamCity 2018, allowing teams to define pipeline configurations using JetBrains’ own language, Kotlin, rather than XML or YAML.
Who:
Developed, licensed, and maintained commercially by JetBrains s.r.o.
Why:
Created to provide professional software development teams with an intelligent, out-of-the-box CI/CD server that analyzes test histories, detects flaky builds, manages agent pools, and offers unified configurations.
Introduction
Advantages
Kotlin DSL — Define pipelines using a fully compiled, statically typed language. Catch syntax errors in your IDE before pushing configuration changes to git.
Advanced Test Analytics — Automatically detects flaky tests, tracks execution speed histories, runs test suites in parallel, and assigns failures to specific developers.
VCS Root Sharing — Define a Version Control System root once and reuse it across hundreds of independent build configurations.
Snapshot Dependencies — Locks the exact git commit hash across all connected pipeline stages, ensuring build consistency across multi-stage jobs.
Pre-tested Commits (Remote Run) — Run builds on agents directly from your IDE before committing changes to the remote repository.
Disadvantages
Commercial Licensing Costs — While a free professional version exists (limited to 100 build configurations and 3 agents), enterprise usage scales in cost.
Heavy Resource Footprint — The Java server backend requires significant RAM and CPU hosting compared to lightweight SaaS integrations.
Steep Learning Curve for Kotlin DSL — Configuring builds via Kotlin requires familiarity with Kotlin programming paradigms compared to simple YAML files.
Learning Guidelines
Build Chains Concept — Unlike step-by-step scripts, TeamCity splits pipelines into modular Build Configurations linked together via Snapshot and Artifact dependencies.
Agent Requirements — Ensure your configurations specify execution requirements (e.g. exists.sdk.version = 17) to prevent builds from failing due to missing agent compilers.
Build Chains: Snapshot vs. Artifact Dependencies
Dependency DAG Mechanics
TeamCity builds pipelines using Build Chains (Directed Acyclic Graphs). These rely on two dependency models:
Snapshot Dependency: Forces jobs to compile from the exact same revision of source code. If Job B depends on Job A, they will both build from the exact same git commit hash, even if a new commit is pushed while Job A is running.
Artifact Dependency: Downloads compiled file outputs from a preceding job.
TeamCity configurations are stored in the .teamcity/settings.kts file. Below is an educational, production-ready template:
import jetbrains.buildServer.configs.kotlin.*import jetbrains.buildServer.configs.kotlin.buildSteps.scriptimport jetbrains.buildServer.configs.kotlin.triggers.vcs// Initialize project definitionproject { // Register build configurations buildType(CompileAndTest) buildType(DeployToStaging) // Define execution sequence using build chains buildChain { compileAndTest -> deployToStaging }}// 1. First Build Configuration: Compile and Test codeobject CompileAndTest : BuildType({ name = "1. Compile & Test" description = "Compiles code and runs test suites" // Define VCS repository settings vcs { root(DslContext.settingsRoot) // Auto-detects the repository root } steps { // Step 1: Run Shell task script { name = "Install Dependencies" scriptContent = "npm ci" } // Step 2: Run Tests script { name = "Run Unit Tests" scriptContent = "npm run test" } } // Declare output files to make them available for deployment artifactRules = "dist/** => dist.zip" // Trigger configuration: run automatically on git commits triggers { vcs { // Triggers build on any commit to repository } }})// 2. Second Build Configuration: Deploy artifactsobject DeployToStaging : BuildType({ name = "2. Deploy to Staging" description = "Deploys compiled artifacts to hosting servers" steps { script { name = "Deploy Build" scriptContent = "./deploy.sh dist.zip" } } dependencies { // Establish Snapshot dependency (enforces code consistency) dependency(CompileAndTest) { snapshot { onDependencyFailure = FailureAction.FAIL_TO_START } // Establish Artifact dependency (downloads output files) artifacts { cleanRules = true artifactRules = "dist.zip => ." } } }})
Build Agents & Requirements
Executor Filtering
TeamCity agents pull builds dynamically. To prevent a build from running on an incompatible agent, define Agent Requirements:
object CompileAndTest : BuildType({ name = "Compile & Test" // ... (VCS and Steps definitions) // Enforce build agent criteria requirements { // Agent must run Linux equals("teamcity.agent.jvm.os.name", "Linux") // Agent must have Node.js 18 installed exists("node.js.18") // Agent must have Docker installed equals("docker.server.version", "20.10.12") }})
Teaching Note: Key Concepts & Gotchas
Why use Kotlin DSL over YAML?
Standard YAML configurations are simple string dictionaries. Kotlin DSL, however, is a compiled language program.
If you make a typo in a YAML tag, the pipeline fails only when triggered on the server.
With Kotlin DSL, you can open .teamcity/ in IntelliJ IDEA, write your scripts, and get auto-completion, refactoring tools, and compile-time validation before committing code.
Remote Run Feature
One of TeamCity’s unique advantages. Developers can install the TeamCity IDE plugin, choose “Remote Run”, and select files they are editing locally.
TeamCity sends these files to a build agent and runs a test pipeline without committing the changes to git.
If the build passes, the IDE can automatically commit and push the changes to git.
Flaky Test Quarantining
TeamCity monitors test histories automatically. If a test passes and fails on the same commit in different builds, it marks the test as flaky.
Administrators can quarantine flaky tests so they don’t block release deployments while developers investigate the root cause.