Post

03. DevOps about Code

03. DevOps about Code

DevOps about Code


Prerequisites


1. What is Code in DevOps?

The Code phase is where the planned system starts becoming real.

This is not just about writing something that works. It is about writing software that is:

  • correct
  • maintainable
  • testable
  • performant
  • ready to move through the next stages of the pipeline

Code is the phase where requirements are translated into reliable, maintainable, and production-ready source code.

2. Why the Code Phase Matters

In DevOps, code is not isolated from the rest of the lifecycle.

Bad code does not only create bugs. It also creates problems for:

  • build stability
  • testing
  • deployment
  • operations
  • monitoring
❌ Poor coding leads to:
  • unclear ownership
  • hard-to-test modules
  • fragile builds
  • hidden performance issues
  • difficult debugging in production
✔ Good coding leads to:
  • cleaner builds
  • easier testing
  • safer refactoring
  • predictable performance
  • faster delivery

3. Goals of the Code Phase

When writing code for this project, the goals are:

  1. implement the required functionality
  2. preserve low latency
  3. minimize unnecessary memory operations
  4. keep modules testable and replaceable
  5. make the system safe for long-running production use

4. Core Principles in the Code Phase

4-1. Write code that matches the architecture

Do not start by writing random helper functions. Follow the pipeline defined in the planning phase.

For example:

1
2
3
4
5
6
7
8
9
Input Frame
   ↓
Preprocessor
   ├─ Resize
   ├─ Normalize
   ├─ Filter
   └─ ROI Extract
   ↓
Output Frame

Each stage should have a clear responsibility.

4-2. Separate responsibilities clearly

A preprocessing system becomes difficult to maintain when one class does everything.

For example:

  • frame acquisition → separate module
  • preprocessing logic → separate module
  • queue handling → separate module
  • metrics/logging → separate module
❌ Bad idea
1
2
3
4
5
class ImageProcessor
{
public:
    void CaptureResizeFilterLogQueueAndSend();
};

Too many responsibilities are mixed together.

✔ Better idea
1
2
3
4
class FrameSource;
class Preprocessor;
class FrameQueue;
class MetricsCollector;

This makes the system easier to test, optimize, and replace.

4-3. Prefer explicit data flow

In performance-sensitive C++ systems, hidden behavior is dangerous.

Avoid:

  • unnecessary copies
  • unclear ownership
  • implicit global state
  • surprise allocations
✔ Good practice
  • pass large buffers by reference or view
  • define ownership clearly
  • avoid global mutable state
  • keep memory lifetime predictable

6. Example Design for the Preprocessing Module

A simple interface might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Frame
{
    std::vector<uint8_t> pixels;
    int width;
    int height;
    int channels;
};

class Preprocessor
{
public:
    Frame Process(const Frame& input);

private:
    Frame Resize(const Frame& input);
    void Normalize(Frame& frame);
    void Filter(Frame& frame);
    Frame ExtractROI(const Frame& input);
};

This is easy to understand, but it may not yet be optimal.

7. Coding with Performance in Mind

For real-time C++ systems, the code phase must already consider optimization.

Not premature micro-optimization, but structural performance awareness.

7-1. Minimize copies

Image data is large. Careless copying can destroy throughput.

Risky pattern
1
Frame Resize(Frame input);

This may copy the whole frame.

✔ Better pattern
1
Frame Resize(const Frame& input);

Or use preallocated output buffers when possible.

7-2. Avoid repeated allocations

Allocating memory every frame can become expensive in real-time systems.

Instead of:

  • allocating temporary buffers inside tight loops
  • resizing vectors repeatedly
  • constructing heavy objects per frame

Prefer:

  • preallocated buffers
  • object reuse
  • fixed-capacity structures when possible

7-3. Keep hot paths simple

The critical frame-processing path should be easy for both the CPU and the engineer.

Avoid putting too much into the hot loop:

  • logging
  • dynamic dispatch if unnecessary
  • complex branching
  • unrelated bookkeeping

7-4. Write code that can be optimized later

If SIMD or multithreading may be added later, the code structure should allow it.

For example:

  • isolate pixel operations
  • keep data layout consistent
  • avoid tightly coupling stages
  • make work units parallelizable
Example: A More Optimization-Friendly Structure
1
2
3
4
5
6
7
8
9
10
class Preprocessor
{
public:
    bool Process(const Frame& input, Frame& output);

private:
    bool Resize(const Frame& input, Frame& output);
    bool Normalize(Frame& frame);
    bool Filter(Frame& frame);
};

Why is this better?

  • avoids unnecessary return-value objects
  • allows buffer reuse
  • makes ownership clearer
  • fits better with real-time pipelines

8. Code Quality in DevOps Means More Than Syntax

In DevOps, “good code” includes more than clean formatting.

It should also support:

  • automated builds
  • unit testing
  • code review
  • CI pipelines
  • production debugging

9. Common Mistakes

❌ Writing without module boundaries

Leads to tightly coupled code

❌ Mixing business logic with infrastructure logic

Makes testing and maintenance difficult

❌ Ignoring memory behavior

Causes performance regression

❌ Overengineering too early

Makes the code hard to deliver and maintain

❌ Optimizing blindly

Can waste time without solving the real bottleneck

This post is licensed under CC BY 4.0 by the author.