Skip to content

Instantly share code, notes, and snippets.

@vishalzambre
Created January 26, 2026 05:08
Show Gist options
  • Select an option

  • Save vishalzambre/d7406adbe07552196f8058993e626ae6 to your computer and use it in GitHub Desktop.

Select an option

Save vishalzambre/d7406adbe07552196f8058993e626ae6 to your computer and use it in GitHub Desktop.
Mock Interview: Advanced Ruby (Pure Ruby / Automation)

🧠 Mock Interview: Advanced Ruby (Pure Ruby / Automation)

1. Ruby Object Model

Q1. How does Ruby resolve a method call?

Model Answer

When Ruby receives a method call, it starts lookup from the object’s singleton class, then moves to the object’s class, followed by any prepended modules, then included modules (in reverse order), and continues up the superclass chain until BasicObject.

If the method isn’t found, Ruby calls method_missing. A correct implementation must also override respond_to_missing? to stay consistent.

In production systems, especially automation engines, I avoid relying heavily on method_missing because it hurts performance and makes debugging harder. I prefer generating methods explicitly using define_method.


Q2. What is a singleton class and when would you use it?

Model Answer

A singleton class allows defining behavior on a single object instance, not the entire class.

I use singleton classes when:

  • Attaching runtime behavior to a specific workflow step
  • Decorating an object with instrumentation or metadata
  • Memoizing behavior per instance

In automation platforms, this is useful when steps need dynamic behavior without polluting global classes.


2. Blocks, Procs, Lambdas & DSLs

Q3. Difference between Proc and lambda?

Model Answer

The biggest differences are control flow and arity.

A lambda enforces arity strictly and its return exits only the lambda. A Proc is lenient with arguments and a return exits the enclosing method.

For workflow engines, I prefer lambdas because they behave more like functions — safer, predictable, and easier to compose.


Q4. How would you design a Ruby DSL for workflows?

Model Answer

I separate definition from execution.

The DSL builds an in-memory representation of the workflow using a context object and instance_eval. The workflow is validated first, then executed by an engine that interprets that structure.

I avoid executing logic directly inside the DSL to keep definitions deterministic and testable.

This pattern also allows versioning workflows and replaying executions safely.


Q5. What are the dangers of instance_eval?

Model Answer

instance_eval changes self, which can:

  • Leak internal state
  • Hide method origins
  • Create security risks if inputs are user-controlled

To mitigate this, I limit DSL scope, avoid exposing global methods, and validate the resulting structure instead of trusting execution during definition.


3. Metaprogramming

Q6. When would you prefer define_method over method_missing?

Model Answer

Almost always.

method_missing adds runtime overhead and makes stack traces harder to understand. I use it only as a fallback or discovery mechanism, then generate real methods with define_method and cache them.

This approach is faster, clearer, and safer for long-running automation processes.


Q7. How would you dynamically define API connector methods?

Model Answer

I’d generate methods at load time based on connector metadata.

Each method would encapsulate:

  • Endpoint
  • HTTP verb
  • Input validation
  • Response normalization

I’d namespace methods per connector to avoid collisions and ensure method names are deterministic, so errors are surfaced early rather than at runtime.


Q8. How do you safely override existing behavior in Ruby?

Model Answer

I prefer Module#prepend.

It keeps the original method intact and makes the override explicit in the method lookup chain. It’s much safer than monkey patching because it’s reversible, testable, and easier to reason about.

I avoid global monkey patches in automation systems due to unpredictable side effects.


4. Concurrency & Parallelism

Q9. Explain Ruby’s GIL and its impact.

Model Answer

MRI Ruby uses a Global Interpreter Lock, so only one thread executes Ruby bytecode at a time.

However, threads still help with I/O-bound workloads, which is common in automation platforms that make many API calls.

For CPU-bound tasks, I’d use processes or offload work to native extensions or external services.


Q10. How would you execute thousands of automations concurrently?

Model Answer

I’d use:

  • A bounded thread pool
  • A queue-based execution model
  • Backpressure to avoid overload

Each job must be idempotent and retry-safe. External calls should be wrapped with timeouts and retries, and execution state must be persisted so jobs can resume after failure.


Q11. How do you make Ruby code thread-safe?

Model Answer

The best approach is to avoid shared mutable state.

When sharing is unavoidable, I use:

  • Immutable objects
  • Thread-safe structures like Concurrent::Map
  • Mutexes sparingly and locally

In automation engines, isolation per execution is critical to avoid cross-job contamination.


5. Memory & Performance

Q12. How does Ruby’s garbage collector work?

Model Answer

Ruby uses a generational, mark-and-sweep GC.

Most objects die young, so minor GCs are frequent and cheap. Long-lived objects are promoted and collected less often.

In long-running processes, controlling object allocation and avoiding unnecessary temporary objects is key to stable memory usage.


Q13. How would you debug a memory leak?

Model Answer

I’d start by comparing heap snapshots over time.

Using ObjectSpace, I’d identify retained objects, especially strings, arrays, and hashes. I pay special attention to caches, class-level variables, and dynamically created symbols.

Leaks in Ruby are usually caused by references that never get released, not the GC itself.


Q14. When are symbols dangerous?

Model Answer

Before Ruby 2.2, symbols were not garbage-collected, so dynamically creating symbols could leak memory.

Even today, dynamically generating symbols from user input is risky and unnecessary. Strings are safer for unbounded input.


6. Error Handling & Reliability

Q15. How would you design retries?

Model Answer

Retries should be explicit and selective.

I’d classify errors into retryable and non-retryable, use exponential backoff with jitter, and ensure operations are idempotent.

Blind retries can cause duplicate actions, especially in integrations, so idempotency keys are critical.


Q16. Why is rescuing StandardError everywhere bad?

Model Answer

It hides bugs and makes failures invisible.

Instead, I rescue specific exceptions, log with context, and re-raise when appropriate. In automation systems, observability is more important than suppressing errors.


Q17. How do you build fault-tolerant workflows?

Model Answer

Each step should be isolated and checkpointed.

Failures should:

  • Not corrupt global state
  • Be retryable independently
  • Support compensation logic when needed

This allows workflows to resume safely without re-running successful steps.


7. Architecture (Pure Ruby)

Q18. How do you structure a large Ruby system without Rails?

Model Answer

I rely on explicit boundaries:

  • Namespaced modules
  • Clear ownership per component
  • Dependency injection instead of globals

I avoid magic and prefer explicit requires so boot behavior is predictable.


Q19. How do you handle configuration?

Model Answer

Configuration should load once at boot, be validated, and exposed via immutable objects.

I avoid reading ENV variables at runtime inside business logic to keep behavior deterministic and testable.


8. Serialization & Data

Q20. How do you process very large JSON payloads?

Model Answer

I avoid loading the entire payload into memory.

Instead, I use streaming parsers and process data incrementally, yielding records as they arrive. This keeps memory stable and allows early failure detection.


9. Testing & Observability

Q21. How do you test metaprogrammed code?

Model Answer

I test behavior, not implementation.

I assert that generated methods exist and behave correctly. I also add contract tests to ensure new connector definitions don’t break existing workflows.


Q22. How would you add observability to an automation engine?

Model Answer

I’d add:

  • Correlation IDs per execution
  • Structured logs per step
  • Metrics for latency, retries, and failures

This allows tracing a single automation across distributed systems.


10. Opinionated Questions

Q23. What Ruby feature do you avoid in production?

Model Answer

Heavy use of method_missing.

It hides intent, hurts performance, and complicates debugging. I prefer explicit code generation and clear APIs.


Q24. Ruby vs JVM for automation platforms?

Model Answer

Ruby excels at expressiveness and developer velocity, which is ideal for DSLs and connectors.

The JVM offers better raw performance and concurrency but at the cost of complexity. For integration-heavy automation, Ruby’s strengths outweigh its limitations when systems are designed carefully.


Q25. How do you design idempotent automation steps?

Model Answer

Each step must have a deterministic identity based on inputs.

Before executing, I check whether the step has already completed successfully. External calls use idempotency keys when supported, ensuring safe retries and replays.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment