QA - Section 1.01. Smart Pointer and API
QA - Section 1.01. Smart Pointer and API”
1. What are the key differences between std::unique_ptr and std::shared_ptr, and when would you choose one over the other?
The main difference is ownership.
std::unique_ptr has exclusive ownership, so only one pointer owns the object. It’s lightweight and faster, so I use it by default when ownership is clear.
std::shared_ptr allows shared ownership using reference counting. I use it only when multiple parts of the system need to access the same object.
Also, I’m careful with shared_ptr because it can cause cyclic references, so I use weak_ptr when needed.
2. When two objects need to reference each other, how do you prevent a std::shared_ptr reference cycle and the resulting memory leak?
To prevent a shared_ptr reference cycle, I would replace one side with std::weak_ptr.
If two objects hold shared_ptr to each other, their reference counts never reach zero, so the memory is never released.
A weak_ptr allows one object to refer to the other without owning it, which breaks the cycle and prevents the memory leak.
3. weak_ptr breaks ownership loops. Switching topics, can you explain the Rule of Five, and in what situations you would explicitly define all five special member functions
If a class owns a resource, I need to define how ownership is copied, moved, and destroyed.
The Rule of Five means that if a class defines one of the special member functions related to resource management, it often needs to consider defining all five:
destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator.
I would explicitly define all five when the class directly manages a resource such as dynamic memory, a file handle, or some other non-copy-safe resource.
In that case, I need to control how the resource is copied, moved, and released safely.
If I use RAII types like std::vector, std::string, or std::unique_ptr, I usually prefer the Rule of Zero instead.
4. What’s the difference between authentication and authorization, and where would you enforce each in a backend service
Authentication happens first, then authorization.
Authentication is about verifying who the user is, while authorization is about checking what the user is allowed to do.
In a backend service, I usually enforce authentication at the entry point, such as middleware or an API gateway, by validating tokens.
Authorization is enforced closer to the business logic, typically in the service or controller layer, where I check whether the user has permission to access or modify a resource.
5. What mechanisms do you use to control access and permissions (e.g., tokens, API keys, OS-level ACLs), and where is that enforced
I typically use token-based authentication such as JWT for user-level access, and API keys for service-to-service communication.
Authentication is enforced at the entry point, such as middleware or an API gateway, where tokens or API keys are validated.
Authorization is enforced closer to the business logic, in the controller or service layer, where I check if the user has permission to perform a specific action.
For lower-level access control, such as file or system resources, I rely on OS-level mechanisms or cloud IAM policies.
6. How would you design an API key validation and rotation flow so you can revoke a compromised key without downtime across multiple backend instances?
To keep request and response contracts aligned over time, I start with a clear API contract, for example using a schema or API specification.
Then I try to keep changes backward compatible. Instead of removing or renaming fields immediately, I usually add new fields first and deprecate old ones gradually.
I also like to validate the contract automatically, for example through shared schemas, generated types, or contract tests, so mismatches are detected early during development or CI.
For larger changes, I would use versioning or a phased rollout, so the front end and back end do not have to change at exactly the same time.
7. How would you design an API key validation and rotation flow so you can revoke a compromised key without downtime across multiple backend instances?
I would use a centralized key store so all backend instances validate against the same source of truth.
Keys would be stored in hashed form with metadata like status and expiration. Validation would happen at the gateway or middleware layer, with only short-lived caching for performance.
For rotation, I would create a new key first and allow both keys during a short transition period. If a key is compromised, I would revoke it centrally and push cache invalidation or revocation events to all instances, so the key is rejected immediately without downtime.
8. Practically what do you put in place to enforce that loose coupling?
Practically, I enforce loose coupling in a few ways.
First, I define a clear API contract, such as a schema, so both the front end and back end depend on an explicit interface rather than assumptions.
Second, I try to keep changes backward compatible. Instead of removing or renaming fields, I usually add new fields and deprecate old ones gradually.
Third, I use validation or contract tests to catch mismatches early, ideally in CI.
And for larger changes, I use versioning or feature flags so both sides can evolve independently without requiring synchronized releases.
9. When you build a custom module, what criteria do you use to decide its boundaries?What goes inside the module versus exposed as an interface?
When I build a custom module, I usually decide the boundary based on responsibility, complexity, and how the module will be used by others.
Inside the module, I keep the implementation details, internal algorithms, helper logic, and anything that may change frequently.
At the interface level, I expose only the minimal functions and parameters that users actually need.
My goal is to make the module easy to understand and hard to misuse. So I prefer clear function names, limited inputs, and stable behavior, while hiding internal complexity.
I also try to group things that change together into the same module, and separate things with different responsibilities. That helps reduce coupling and makes maintenance easier.