Table of Contents
Knowledge Base
Knowledge
Interview Questions
Knowledge
Data Engineering
Parquet
What it is
Apache Parquet is a binary, columnar file format for tabular data. It describes how data is laid out inside files; it is not a database, query engine, catalog, or transaction manager.
Why it matters
Analytical workloads commonly read a few columns across many rows. Keeping values for each column together reduces I/O, improves compression, and lets engines skip data that a query cannot use. Parquet is therefore a common storage format in data lakes on Amazon S3 and similar object stores.
How it works
CSV files store text rows and usually require parsing every field that is read. Row-based storage similarly keeps all fields for one record together, which is useful when an application frequently reads or changes complete records. Parquet instead groups values by column in column chunks and pages, with metadata describing their location and encoding.
For example, for SELECT AVG(salary) FROM employees, an engine can use column pruning to read the salary column without reading name, address, or other unrelated columns. Repeated values, similar values, and typed binary representations also make a column a strong target for compression and encoding.
Example
Spark, Amazon Athena, and Databricks can query Parquet stored in a data lake. The same files are useful as efficient training or feature data for analytics and machine-learning workflows because engines can scan only required columns and partitions.
Trade-offs and limitations
Parquet is optimized for large scans and batch writes, not frequent single-record updates. Updating one row generally means rewriting the file or files that contain it, then coordinating which file version readers should use. A table format such as Iceberg or Delta Lake adds that coordination layer; Parquet alone does not.
Related topics
Glossary
- Amazon S3: Amazon Simple Storage Service, an object-storage service.
- CSV: Comma-separated values, a plain-text tabular file format.
- I/O: Input/output operations, such as reading from or writing to storage.
Sources
Lakehouse
What it is
A lakehouse combines low-cost object storage with table-management features expected by analytical databases. Amazon S3 is object storage: it stores objects, but it does not by itself define multi-file table transactions, isolation, or table rollback.
Apache Iceberg and Delta Lake are table formats that place a transactional table layer over data files, commonly Parquet. Iceberg uses versioned metadata and manifests; Delta Lake uses a versioned transaction log. Both make a collection of files behave as one table.
Why it matters
Without a table layer, a reader can observe a partially updated set of files while a writer is changing a dataset. A table format gives readers a consistent snapshot, supports ACID properties, allows concurrent writers to detect conflicts, and makes historical versions available for time travel and rollback.
How it works
Writers do not modify existing object files in place. They write replacement data files and prepare new metadata that points to them. The table then atomically commits a new snapshot or log version. Readers resolve the current table version from metadata and read only the files referenced by that version.
Writes normally use optimistic concurrency control: a writer plans against a current version, then validates and retries or fails if another compatible commit has not occurred. This avoids holding a long-lived global lock while data files are written.
Example
Assume snapshot 17 references A.parquet, B.parquet, and C.parquet. To change data held by B.parquet, a writer produces B2.parquet and commits snapshot 18, which references A.parquet, B2.parquet, and C.parquet. Readers on snapshot 17 continue to see B.parquet; new readers see B2.parquet. Rolling back selects an earlier valid snapshot, subject to metadata and file-retention policies.
Trade-offs and limitations
An update to one logical row can still rewrite a data file, so file sizing, partitioning, compaction, and write patterns matter. Transactional guarantees come from the table format and its catalog or log protocol, not from Parquet or S3 alone. Iceberg and Delta Lake have different metadata designs, integrations, and operational requirements.
Related topics
Glossary
- ACID: Atomicity, consistency, isolation, and durability; transaction properties that preserve correct table state.
- Amazon S3: Amazon Simple Storage Service, an object-storage service.
Sources
Distributed Systems
Circuit Breaker
What it is
A circuit breaker is a resilience pattern that temporarily stops calls to a failing or unhealthy dependency. It protects callers from repeatedly waiting on work that is unlikely to succeed.
Why it matters
In the chain Service A → Service B → Service C, a slow Service C can cause requests in Service B to wait until their timeouts expire. Those waiting requests consume threads, connections, memory, and queues. Service B then slows or fails, and Service A starts timing out too: a cascading failure.
How it works
In the CLOSED state, calls flow normally while the breaker observes failures, including timeouts. After a configured threshold, it moves to OPEN and fails fast rather than calling the dependency. The caller can return a fallback, cached result, a clear error, or degrade a nonessential feature.
After a cooldown, the breaker enters HALF-OPEN and permits a limited number of trial calls. Successful trials close the circuit; continued failures reopen it. Timeouts, bounded retries, bulkheads, and sensible fallback behavior complement the circuit breaker.
Example
If Service C becomes extremely slow, Service B opens its circuit after repeated timeouts. Requests from Service A receive a prompt fallback or failure from Service B instead of accumulating waiting work. This preserves resources and confines the outage.
Trade-offs and limitations
Thresholds that are too aggressive can reject healthy traffic during short disruptions; thresholds that are too lenient protect the system too late. A fallback must be safe and honest about freshness. The pattern does not repair Service C; it limits the blast radius while it recovers.
Related topics
- Timeouts
- Retries with backoff
- Bulkheads
Sources
Interview Questions
Data Engineering
Lakehouse
Updating a Single Row in a Billion-Row Parquet Table
Question
If a table stored as Parquet files in S3 has billions of rows and the business needs to update one row with ACID transactions and rollback, how would you implement it?
Short interview answer
S3 itself does not provide table transactions. I would use a table format such as Apache Iceberg or Delta Lake. Data stays in Parquet files, while versioned metadata defines table snapshots. An update writes new files and atomically commits a new snapshot, giving isolation, rollback and time travel.
Detailed answer
Assume an orders table is stored as files in S3:
s3://data/orders/
part-001.parquet ← 1,000,000 orders
part-002.parquet ← 1,000,000 orders
part-003.parquet
...
Order 42 is in part-002.parquet:
order_id=42, delivery_address="Old Street", status="shipped"
The business wants to correct the address. Parquet cannot edit that one value in place, and S3 cannot tell every reader, “these are the exact files that make up version 17 of the table.” Replacing a file directly risks readers observing a half-finished update.
Iceberg or Delta Lake adds versioned table metadata above the Parquet files. Instead of treating a table as “every .parquet file in this folder,” readers use one named table version:
table
↓
metadata or transaction log
↓
snapshot 17
↓
part-001.parquet, part-002.parquet, part-003.parquet
The engine reads the table metadata, finds part-002.parquet, and writes a replacement. It does not overwrite the original file.
Before commit — snapshot 17
part-001.parquet
part-002.parquet ← contains order 42 with "Old Street"
part-003.parquet
After commit — snapshot 18
part-001.parquet
part-002-v2.parquet ← contains order 42 with "New Street"
part-003.parquet
The atomic operation is the small metadata change from snapshot 17 to snapshot 18. A report that started on snapshot 17 keeps reading the old file. A report that starts after the commit reads the new file. Neither report sees a mixed table.
If another writer also changed part-002.parquet, the table format notices that snapshot 17 is no longer current. The second writer retries using the latest version or fails with a conflict; it does not silently erase the first update.
If the address correction was wrong, snapshot 17 is still available for time travel or can be restored as the current version. This is a good design for analytical data with occasional corrections. If the system performs constant low-latency row updates, an operational database should usually own the live record and the lakehouse should receive the analytical copy.
Related knowledge
Sources
Iceberg vs. Delta Lake
Question
How would you compare Apache Iceberg and Delta Lake for a lakehouse table?
Short interview answer
Both add transactional table semantics over data files such as Parquet: versioned table state, atomic commits, concurrent-write handling, and historical versions. Iceberg manages table state through metadata and manifests, while Delta Lake uses a versioned transaction log. The choice depends on engine ecosystem, catalog and governance needs, and operational fit.
Detailed answer
Both formats solve the same problem: this is only a folder of files, not a table with transactions.
s3://analytics/orders/
part-001.parquet
part-002.parquet
part-003.parquet
Neither file name says which files belong together after an update, which version a reader should use, or how two writers avoid overwriting one another.
Both formats add a versioned definition of the table, but they record it differently:
Iceberg Delta Lake
------- ----------
metadata/v17.metadata.json _delta_log/00000000000000000017.json
metadata/snap-17.avro _delta_log/00000000000000000018.json
data/part-001.parquet part-001.parquet
data/part-002.parquet part-002.parquet
In an Iceberg table, metadata files and manifests describe the current snapshot. In a Delta Lake table, the transaction log records the ordered changes that produce the current version. In both cases, a writer creates data files first and then publishes one new table version.
Imagine a company where the same orders table must be read and written by Spark jobs, Trino analysts, and Flink streaming jobs. I would test the exact versions of those engines and catalogs against both formats. The winning format is the one all of those tools can operate reliably, including schema changes, maintenance, and concurrent writes.
On the other hand, if a team already runs a platform with mature Delta Lake operations and the main writers and readers are in that ecosystem, Delta Lake may reduce operational friction. If broad interoperability across engines and catalogs is the key constraint, Iceberg may fit better. The point is not “Iceberg always wins” or “Delta always wins”; it is to choose the table format that matches the platform the team actually runs.
Related knowledge
Sources
Parquet Update Strategy
Question
Why is Parquet a poor choice for frequent single-record updates, and what strategy would you use instead?
Short interview answer
Parquet is a columnar file format optimized for analytical scans and batch writes, not in-place row updates. Updating one record usually requires rewriting an affected file. Use a table format such as Iceberg or Delta Lake to coordinate rewritten files as atomic table versions, or choose an operational database when frequent point updates are the primary workload.
Detailed answer
An employees Parquet file stores values by column. For an analytical query, that is exactly what we want:
SELECT AVG(salary) FROM employees;
Query needs: salary
Parquet reads: salary column
Parquet skips: employee_id, name, address, department
Reading one column instead of every field reduces I/O and often compresses better. This is why Parquet is a strong format for reports and large scans.
Now change one employee's address:
employees-part-08.parquet
employee_id=41, address="Berlin"
employee_id=42, address="Gdansk" ← change this one value
employee_id=43, address="Paris"
Parquet cannot open that file and patch the one record. The usual result is a replacement file:
Before: employees-part-08.parquet
After: employees-part-08-v2.parquet
If the files form an analytical table, Iceberg or Delta Lake should publish that replacement through a new snapshot. Readers then see the entire old table or the entire corrected table, with a rollback option if the correction was wrong.
If the product changes employee records constantly, I would keep those records in an operational database. Parquet remains the analytical copy, where its columnar layout is an advantage rather than an obstacle.
Related knowledge
Sources
Distributed Systems
Circuit Breaker
Slow Downstream Service
Question
Service A calls Service B, which calls Service C. Service C becomes extremely slow and Service A starts failing because of timeouts. What pattern would you implement?
Short interview answer
Implement a Circuit Breaker so Service B stops calling the unhealthy Service C after repeated failures or timeouts. It fails fast instead of waiting for more timeouts, preventing resource exhaustion and cascading failure back to Service A.
Detailed answer
I would put a circuit breaker around Service B's call to Service C. The problem is not only that C is slow; it is that every request waiting on C consumes a connection, a thread, memory, and queue capacity in B. If enough calls wait until they time out, B becomes unhealthy too, and A starts failing even though A may have no fault of its own.
Imagine C normally answers in 100 milliseconds but suddenly takes 30 seconds. At first, the breaker is CLOSED, so B calls C as usual and records the timeouts. Once failures cross a threshold, the breaker moves to OPEN. B stops sending requests to C and immediately returns a safe fallback, cached result, or clear failure. That fail-fast response frees B to continue serving the work it still can handle.
Timeout: 1 second Failure threshold: 5 timeouts in 10 seconds
09:00:00 B → C times out breaker: CLOSED
09:00:02 B → C times out breaker: CLOSED
09:00:08 Fifth timeout breaker: OPEN
09:00:09 B → C is skipped B returns a fallback immediately
09:00:38 One trial request breaker: HALF-OPEN
After a cooldown, the breaker becomes HALF-OPEN and allows only a small number of trial calls. If C is healthy again, the breaker closes and normal traffic resumes. If those calls still time out, it opens again rather than releasing a backlog of requests onto a dependency that has not recovered.
I would set the timeout below B's own request deadline, use bounded retries, and decide explicitly what an acceptable fallback is. A cached product description may be fine; a cached payment authorization is not. The circuit breaker does not repair C, but it contains the failure and prevents C from taking A and B down with it.
Related knowledge
Sources
C#
Basics
Value Types vs. Reference Types
Question
Describe the differences between reference types and value types.
Short interview answer
A value-type variable contains its value, so assignment copies that value. A reference-type variable contains a reference to an object, so assignment copies the reference and both variables can refer to the same object.
Detailed answer
int, bool, enum, and struct are value types. class, string, array, delegate, and interface types are reference types. The practical difference is copy behavior:
var left = new Point { X = 1 };
var right = left;
right.X = 2; // left.X is still 1.
var first = new Customer { Name = "Ada" };
var second = first;
second.Name = "Grace"; // first.Name is now "Grace" too.
The first assignment copies a value; the second copies a reference. Storage location is an implementation detail; do not reduce the distinction to “stack versus heap.”
Sources
Creating a Custom Value Type
Question
Can we create our own value type?
Short interview answer
Yes. Define a
structor anenum. A struct is a value type and can encapsulate data and related behavior.
Detailed answer
Use a struct for a small, data-centric value such as a coordinate, amount, or identifier:
public readonly record struct CustomerId(Guid Value);
var first = new CustomerId(Guid.NewGuid());
var second = first; // Copies the value.
Immutable structs avoid surprising copy-and-mutate behavior. A struct can implement interfaces, but it cannot inherit from a class or another struct and cannot be a base class.
Sources
String Immutability
Question
Can a
stringbe modified?
Short interview answer
No. A
stringis immutable: an operation that appears to change it creates a new string value instead.
Detailed answer
string is a reference type, but its contents cannot be changed after construction:
var name = "Ada";
name += " Lovelace"; // A new string is created; "Ada" was not modified.
This makes strings safe to share, but repeated concatenation in a loop can create unnecessary allocations.
Sources
Choosing StringBuilder
Question
When would you use a
StringBuilder?
Short interview answer
Use
StringBuilderwhen performing many string modifications, especially in loops or when the final size is unknown. It avoids creating a new string for every intermediate change.
Detailed answer
For a few concatenations, ordinary string interpolation is usually clearer. When building a large report line by line, each + operation would create another immutable string:
var builder = new StringBuilder();
foreach (var row in rows)
builder.AppendLine($"{row.Id}: {row.Name}");
var report = builder.ToString();
StringBuilder maintains a mutable buffer and creates the final string when ToString() is called.
Sources
Interface vs. Abstract Class
Question
What are the differences between an
interfaceand anabstract class?
Short interview answer
An interface defines a contract that unrelated types can implement, and a type can implement multiple interfaces. An abstract class is a base class that can share state, constructors, and implementation, but a class can inherit from only one base class.
Detailed answer
Use an interface for a capability such as ILogger or IComparable<T> that may apply to unrelated types. For example, both FileLogger and DatabaseLogger can implement ILogger while sharing no base implementation. Use an abstract class when derived types belong to one hierarchy and share protected members, fields, constructors, or default behavior: PaymentProcessor can keep common retry configuration while CardProcessor and BankTransferProcessor provide their differing steps. Modern interfaces can provide default members, but they still do not provide instance fields or constructors.
Sources
Implementing Colliding Interface Members
Question
In which ways can you implement multiple interfaces that have a common method signature?
Short interview answer
One public method can implicitly implement compatible members from multiple interfaces. Use explicit interface implementation when each interface needs a separate implementation or when the member should be available only through the interface.
Detailed answer
If IAudit.Save() and ICache.Save() mean the same thing, a public Save() can implement both. If they mean different things, implement them explicitly as void IAudit.Save() and void ICache.Save(). Callers must then cast or hold the object as the appropriate interface, which prevents an ambiguous or misleading public API.
Sources
Polymorphism
Question
Describe polymorphism.
Short interview answer
Polymorphism lets code use a base-class or interface contract while the runtime dispatches behavior to the concrete implementation. For example, a collection of
IShapevalues can callDraw()on circles and rectangles without caller-specific branching.
Detailed answer
Subtype polymorphism is the usual C# interview meaning: a variable declared as a base type or interface can refer to different implementations. Virtual methods and interface members enable the runtime to choose the appropriate implementation. This helps separate callers from concrete types, but it should model a real substitutable contract rather than be added for its own sake.
Sources
Virtual Methods and Overrides
Question
Explain
virtualmethods. How can you override them?
Short interview answer
A base class marks an overridable member with
virtual; a derived class supplies a replacement implementation withoverride. Calls through the base-class reference dispatch to the override at runtime.
Detailed answer
For example, Shape.Draw() may be virtual and Circle.Draw() may override it. The override must match the inherited member and cannot reduce its accessibility. Use sealed override when a derived implementation must not be overridden again. Prefer interfaces when the capability is not naturally part of one class hierarchy.
Sources
Overriding, Overloading, and Hiding
Question
Describe the differences between overriding, overloading, and shadowing (hiding).
Short interview answer
Overriding replaces a virtual inherited member for runtime dispatch. Overloading uses the same member name with different parameter lists. Hiding uses
newto introduce a member with the same name, and selection depends on the variable's compile-time type.
Detailed answer
override participates in polymorphism; overloads are selected by argument types and count; hiding depends on the variable’s compile-time type:
class Base
{
public virtual string Virtual() => "base";
public string Hidden() => "base";
}
class Derived : Base
{
public override string Virtual() => "derived";
public new string Hidden() => "derived";
}
Base value = new Derived();
Console.WriteLine(value.Virtual()); // derived
Console.WriteLine(value.Hidden()); // base
A hidden member does not override the base member. Hiding is usually less clear than a deliberate override or a different name.
Sources
is, as, and Cast Expressions
Question
Describe the differences between
is,as, and a cast expression.
Short interview answer
istests type compatibility and can capture a typed variable.asattempts a reference or nullable conversion and returnsnullif it fails. A cast expression performs an explicit conversion and can throw if a runtime reference conversion fails.
Detailed answer
Prefer pattern matching such as if (value is Customer customer) when testing and using a value. Use as only when a null result is the desired failure signal. Use an explicit cast when failure should be exceptional or a non-reference explicit conversion is required, while remembering that a failing reference cast can throw InvalidCastException.
Sources
Equals and GetHashCode
Question
Describe
EqualsandGetHashCode. Is there any correlation between these methods?
Short interview answer
Equalsdetermines equality;GetHashCodeprovides a hash used by hash-based collections. Equal objects must return the same hash code, but unequal objects may collide.
Detailed answer
When overriding value equality, override both methods consistently or implement IEquatable<T>. Dictionary<TKey, TValue> and HashSet<T> use hash codes to locate candidates and then equality to confirm a match:
var key = new CustomerId("42");
var customers = new Dictionary<CustomerId, string> { [key] = "Ada" };
customers.TryGetValue(new CustomerId("42"), out var name); // Works only when both equality and hash code agree.
A hash code must remain stable while an object is used as a key. If CustomerId exposed a mutable field that participated in equality and changed after insertion, the dictionary could no longer find the entry in the expected bucket.
Sources
Hash Code Input Data
Question
Calculating
hashcodeshould be based on what type of data?
Short interview answer
Base a hash code on the same value-defining data used by equality. If two objects are equal according to
Equals, they must produce the same hash code, and the fields used must not change while the object is used as a key in a hash-based collection.
Detailed answer
For an immutable Money value where equality compares currency and amount, both fields belong in the hash calculation. An internal cache timestamp does not, because it is not part of the value’s identity. Conversely, including a mutable property that equality ignores makes lookup behavior difficult to reason about. Hash codes need not be unique; their job is to distribute values so a hash table can narrow its equality comparisons. The essential contract is equal objects have equal hash codes.
Sources
Generic Types and Constraints
Question
Describe generic types and the generic type constraints you know.
Short interview answer
Generics let one type or method work with many types while preserving compile-time type safety. Constraints limit acceptable type arguments, for example
class,struct,new(), a base class, an interface,unmanaged,Enum, orDelegate.
Detailed answer
List<T> is a common example: List<int> accepts integers without runtime casts or boxing. Constraints express requirements needed by the implementation. For example, where T : IComparable<T> permits CompareTo, and where T : new() permits new T(). Static fields in a generic class belong to each closed constructed type, so Cache<int> and Cache<string> have separate static state.
Sources
Generic Type Constraints
Question
Which
generic type constraintsdo you know?
Short interview answer
Generic constraints restrict the type arguments allowed by a generic declaration and expose the operations that the generic code may safely use. Common constraints include base-class, interface,
class,struct,notnull,unmanaged, andnew()constraints.
Detailed answer
Constraints turn an assumption into a compiler-checked contract. For example, where T : IComparable<T> permits generic code to call CompareTo; where T : new() permits it to construct new T(). A type parameter can have one primary category constraint such as class or a base class, followed by interface constraints, and new() has placement rules. Choose the smallest constraint that expresses what the algorithm truly needs. A constraint is not only validation: it defines the members and operations available inside the generic implementation.
Sources
Static Fields in Generic Types
Question
Is there any difference in a
static fieldinside ageneric classin contrast toclass?
Short interview answer
A static field in a generic type is separate for each closed constructed type.
Cache<int>.CountandCache<string>.Countare different static fields; a static field in a non-generic type has one field for that type.
Detailed answer
Cache<T> is a type definition, but Cache<int> and Cache<string> are distinct constructed types at runtime:
public static class Cache<T>
{
public static int Count;
}
Cache<int>.Count++;
Console.WriteLine(Cache<int>.Count); // 1
Console.WriteLine(Cache<string>.Count); // 0
This is useful for per-type caches, but it surprises developers expecting one application-wide counter. If one value must be shared regardless of T, put it in a non-generic type instead.
Sources
Value and Reference Storage
Question
Where in memory are reference and value types stored?
Short interview answer
Do not use “value types are on the stack and reference types are on the heap” as a rule. Value types store their data inline wherever their containing storage is; reference-type variables store references, while their objects are commonly allocated on the managed heap.
Detailed answer
A local value type can be stack allocated, but value-type fields are stored inside their containing object, and value types can be boxed. A reference-type variable may itself be a local or a field; it holds a reference to an object. The meaningful language-level distinction is value versus reference semantics, not one fixed physical memory location.
Sources
Value Type Inheritance
Question
Does a value type support inheritance?
Short interview answer
No. A struct cannot inherit from another class or struct and cannot be a base class. It can implement interfaces.
Detailed answer
All value types derive from System.ValueType in the type system, but user-defined structs are implicitly sealed:
public readonly record struct Point(int X, int Y); // Valid value type.
// public struct ColoredPoint : Point { } // invalid: a struct cannot derive from Point.
This prevents a struct inheritance hierarchy. When a value type needs to satisfy shared behavior, define and implement an interface instead.
Sources
Value Types and Interfaces
Question
Can a value type implement an interface?
Short interview answer
Yes. A struct can implement one or more interfaces, even though it cannot inherit from a class or another struct.
Detailed answer
For example, a value type can implement IComparable<T> or IFormattable. Be aware that assigning a struct to a non-generic interface or object can box it, which may matter in performance-sensitive code.
Sources
Struct Parameterless Constructors
Question
Can you override the default, parameterless constructor of a value type?
Short interview answer
A struct can declare a public parameterless instance constructor, but
default(T)and array initialization still produce the zero-initialized default value and do not call it.
Detailed answer
Modern C# permits an explicitly declared public parameterless struct constructor, but it does not replace the language-defined default value:
var created = new Temperature(); // Calls the declared constructor.
var defaulted = default(Temperature); // Zero-initialized; does not call it.
new Temperature[10] also contains zero-initialized values. Code must therefore remain correct for the default state.
Sources
The string Type
Question
Which kind of type is
string?
Short interview answer
stringis a reference type. Its instances are immutable.
Detailed answer
Copying a string variable copies a reference, but no caller can mutate the string's character data. An expression that appears to change a string creates a new instance. This makes it important not to infer mutability from whether a type is a value or reference type.
Sources
Extension Methods
Question
Explain extension methods. Can an extension method be called on a variable that holds
null?
Short interview answer
Extension methods are static methods called with instance-method syntax. They can be invoked on a
nullreference because the receiver is passed as an argument, but the method must handlenullsafely.
Detailed answer
An extension method does not modify the original type or gain access to its private state:
public static bool IsBlank(this string? value) =>
string.IsNullOrWhiteSpace(value);
var blank = ((string?)null).IsBlank(); // Calls IsBlank(null).
The compiler rewrites the call to a static method with value as the first argument. A null receiver reaches the method; a dereference inside it still throws unless the method checks for null.
Sources
Extension Methods on null
Question
Can
extension methodbe called on variable which holdsnull?
Short interview answer
Yes. Extension-method syntax is compiled as a static method call, so the receiver can be passed as
null. The method must explicitly handle that value before dereferencing it.
Detailed answer
Given public static bool IsMissing(this string? value) => string.IsNullOrEmpty(value);, calling text.IsMissing() is valid even when text is null: the static method receives null as its first argument. An extension method that immediately accesses value.Length will still throw NullReferenceException. This differs from an instance method call, which needs a non-null receiver before its body can run. Use this behavior sparingly and make a null-accepting extension method obvious through its name and nullable annotations.
Sources
yield return and yield break
Question
Describe
yield returnandyield break.
Short interview answer
yield returnproduces the next element of an iterator sequence;yield breakends that sequence. The compiler generates the iterator machinery and execution is deferred until enumeration.
Detailed answer
An iterator method can expose values one at a time without constructing an entire collection first. Its local state is preserved between calls to the enumerator. Use yield return for lazy sequences; use yield break for an early normal end. Exceptions and resource lifetime still need deliberate handling.
Sources
IEnumerable vs. IList
Question
Describe the difference between
IEnumerableandIListor an array. Explain deferred execution.
Short interview answer
IEnumerablerepresents something that can be enumerated; it does not promise indexing, count, or mutation.IListadds indexed access and collection operations. Deferred execution means a query or iterator can run when it is enumerated rather than when it is declared.
Detailed answer
An array and most IList implementations materialize elements and support indexing. An IEnumerable<T> may stream elements from a file, database provider, or iterator method:
var adults = people.Where(person => person.Age >= 18); // No iteration yet.
people.Add(new Person { Age = 30 });
var count = adults.Count(); // The new person can be included here.
The Where call commonly builds a deferred pipeline; enumeration at Count() performs the work. Materialize deliberately with ToList() when a stable snapshot is required.
Sources
IEnumerable vs. IQueryable
Question
Describe the difference between
IEnumerableandIQueryable.
Short interview answer
IEnumerable<T>represents in-memory enumeration using delegates.IQueryable<T>carries an expression tree that a provider can translate, for example into a database query.
Detailed answer
The distinction matters when composing queries against a remote provider:
IQueryable<Customer> query = provider.Customers;
var names = query
.Where(customer => customer.IsActive)
.Select(customer => customer.Name)
.ToList();
The provider can translate the expression tree before ToList materializes results. Calling an arbitrary local method inside Where may not be translatable. Keep filtering and projection in IQueryable<T> while translation is desired, then materialize deliberately before using arbitrary in-memory code.
Sources
throw vs. throw ex
Question
Explain the difference between
throwandthrow ex.
Short interview answer
Inside a
catch, usethrow;to rethrow while preserving the original stack trace.throw ex;resets the stack trace to the current location and makes diagnosis harder.
Detailed answer
Catch an exception only when you can handle it, add meaningful context, or translate it to an appropriate boundary-specific error:
try { SaveInvoice(); }
catch (IOException) { throw; } // Preserves SaveInvoice's stack location.
Replacing the rethrow with throw ex; would make the stack appear to begin in the catch block. Do not use exceptions as ordinary control flow when a normal branch or result can represent the expected condition.
Sources
When to Throw Exceptions
Question
When should you throw an exception? Is there a downside to exception-driven development?
Short interview answer
Throw for exceptional failure states that the caller cannot handle as normal flow. Do not use exceptions for routine expected outcomes; use validation,
Try*APIs, or explicit results where appropriate.
Detailed answer
An invalid argument or impossible object state may justify a specific exception. A missing optional lookup result usually does not. Exceptions carry stack and allocation costs and make normal control flow harder to read; they also obscure which outcomes callers are expected to handle. A pre-check can still race with the operation, so exceptions may remain necessary even when a condition is checked first.
Sources
finally and using
Question
How can you ensure that certain code is always called, regardless of an exception being thrown?
Short interview answer
Use
finallyfor cleanup that must run when atryblock exits. Preferusingorawait usingforIDisposableorIAsyncDisposableresources because they generate the required disposal pattern.
Detailed answer
finally normally runs whether the try succeeds, returns, or throws, although process-terminating failures are exceptional cases:
var stream = File.OpenRead(path);
try { Process(stream); }
finally { stream.Dispose(); }
For a stream or database connection, using expresses the same ownership more clearly and disposes the resource even if the body throws. Do not throw a new exception from finally, because it can hide the original failure.
Sources
Catch-Clause Order
Question
Does the order of catching exceptions matter?
Short interview answer
Yes. Catch more-derived exception types before their base types. A base-type catch first makes later derived catches unreachable.
Detailed answer
For example, the derived type must appear first:
try { OpenConfiguration(); }
catch (FileNotFoundException ex) { CreateDefaultConfiguration(ex); }
catch (IOException ex) { ReportIoFailure(ex); }
catch (Exception ex) { LogUnexpectedFailure(ex); }
Putting catch (IOException) first would make the FileNotFoundException clause unreachable. Order also communicates which failures receive specific recovery behavior and which are only logged at a boundary.
Sources
Choosing a Type for Money
Question
Which type would you use to store a monetary value?
Short interview answer
Usually
decimal, because it has base-10 precision suitable for financial calculations. Store the currency separately when the domain supports multiple currencies.
Detailed answer
Binary floating-point values such as double cannot represent many decimal fractions exactly, which can produce surprising rounding results. decimal is designed for financial and monetary calculations, but it is not a complete money model: specify currency, rounding rules, scale, and whether amounts are net, gross, or tax-inclusive.
Sources
Asynchronous Programming
async and await
Question
Describe the mechanism of
asyncandawait.
Short interview answer
asyncenablesawaitin a method. Awaiting an incomplete task suspends the method and returns control to its caller; the compiler-generated state machine resumes it when the task completes.
Detailed answer
For I/O, await the asynchronous API directly so the calling thread is not blocked. For CPU-bound work that should not occupy a UI thread, use an appropriate background-work strategy such as Task.Run. async does not automatically create a thread; it expresses asynchronous composition around Task or other awaitable operations.
For example, var invoice = await client.GetFromJsonAsync<Invoice>(url, cancellationToken); returns control while the HTTP request is pending; the next line runs only after it produces an invoice, throws, or observes cancellation.
Sources
async void
Question
Can you await an
async voidmethod?
Short interview answer
No. An
async voidmethod has no task for the caller to await. Use it only for event handlers; application code should generally returnTaskorTask<T>.
Detailed answer
Without a returned task, callers cannot await completion, compose the operation, or observe exceptions through normal task handling. Event handlers require void, which is the usual justified exception. An asynchronous method used by application code should expose a task so its lifetime and failures remain observable.
For example, an event handler may be async void SaveButton_Click(...) { await SaveAsync(); }; a service method should instead be async Task SaveAsync() so its caller can await and handle its completion.
Sources
Async Deadlocks
Question
Can tasks cause a deadlock? How can you avoid it?
Short interview answer
Yes. A common deadlock occurs when code blocks on
Task.ResultorWait()while the awaited continuation needs the blocked synchronization context. Preferawaitall the way through the call chain.
Detailed answer
In a UI context, a synchronous wait can block the UI thread while an awaited continuation tries to resume there. Avoid mixing synchronous blocking with asynchronous code. Use await, avoid unnecessary context dependencies in library code, and keep async operations from blocking threads that they need to resume.
For example, a UI thread that calls LoadAsync().Result can block itself while LoadAsync is waiting to resume on that same UI context. Replacing the blocking call with await LoadAsync() leaves the context free to resume the operation.
Sources
ConfigureAwait
Question
Describe
ConfigureAwait.
Short interview answer
ConfigureAwait(false)tells an awaited task not to require resuming on the captured context. It is commonly useful in context-agnostic library code; UI code that updates UI state may need the context.
Detailed answer
ConfigureAwait configures how an await continuation interacts with the context. It is not a universal performance switch or a substitute for correct async design. Choose it based on whether continuation code requires a specific context and follow the conventions of the application framework and library.
For example, a library method that only parses a response can write var text = await response.Content.ReadAsStringAsync().ConfigureAwait(false);; a UI event handler that updates a control after awaiting must preserve its required UI context instead.
Sources
Task Continuations
Question
How can you perform an action after a task finishes without awaiting it? Can
ContinueWithrun only after success?
Short interview answer
You can attach a continuation with
ContinueWith, includingOnlyOnRanToCompletion, butawaitis usually clearer for normal control flow. If the work must outlive the current request, use an explicit background-work design rather than fire-and-forget code.
Detailed answer
ContinueWith exposes scheduling and status options, but it is easier to misuse than await, especially around contexts and exceptions. A continuation restricted to successful completion will not run after a fault or cancellation. Any unawaited operation needs an explicit strategy for error observation, cancellation, lifetime, and shutdown.
For example, await SendReceiptAsync(order); await MarkReceiptSentAsync(order.Id); expresses that the second operation follows successful completion of the first. A continuation API is only needed when its scheduling or completion-state behavior is specifically required.
Sources
Continuations After Success
Question
When using
ContinueWith, can you choose to run your continuation only whentaskhas completed successfully?
Short interview answer
Yes.
ContinueWithaccepts continuation options such asOnlyOnRanToCompletion, butawaitwith normal control flow is usually clearer for application code: code after a successfulawaitruns only if the awaited task completed successfully.
Detailed answer
task.ContinueWith(next, TaskContinuationOptions.OnlyOnRanToCompletion) schedules next only for successful completion. A continuation is not the same as an exception-safe sequential workflow: it needs deliberate handling for cancellation, faults, scheduling, and its returned task. In most ordinary asynchronous methods, write await task; followed by the successful next step; a fault propagates at the await, and cancellation is also observable there. Use continuation APIs where their lower-level scheduling or composition semantics are specifically needed.
For example, task.ContinueWith(next, TaskContinuationOptions.OnlyOnRanToCompletion) does not run next after a fault or cancellation. In most application code, await task; await next(); makes the same success-only flow clearer.
Sources
Cancelling a Task
Question
How can you cancel a task?
Short interview answer
Use
CancellationTokenSourceto signal cancellation and pass itsCancellationTokeninto operations that support it. Cancellation is cooperative: the operation must observe the token and stop.
Detailed answer
Calling Cancel() does not forcibly terminate arbitrary code. The operation checks the token, often by calling ThrowIfCancellationRequested, and returns or throws OperationCanceledException. Propagate tokens through the call chain, dispose the source when appropriate, and distinguish cancellation from faults in the caller's handling.
For example, create a CancellationTokenSource for a request, pass source.Token to the downstream call, and call source.Cancel() when the user leaves the screen. The downstream operation must observe that token; cancellation is cooperative.
Sources
Waiting for Multiple Tasks
Question
You have started multiple tasks without awaiting them. How can you wait for all of them?
Short interview answer
Store the tasks and await
Task.WhenAll(tasks). It completes after every supplied task completes and propagates failures when awaited.
Detailed answer
Create the tasks first, then await Task.WhenAll to allow concurrency where the underlying work supports it. Do not use Task.WaitAll in asynchronous application code because it blocks a thread. If tasks are created with LINQ, materialize the sequence with ToArray or ToList so all operations actually start before awaiting.
For example, start var products = LoadProductsAsync(); and var prices = LoadPricesAsync();, then await Task.WhenAll(products, prices) before combining their results. This allows independent I/O to overlap without hiding either task’s failure.
Sources
TaskCompletionSource
Question
Describe
TaskCompletionSourceand when it can be useful.
Short interview answer
TaskCompletionSource<T>lets code manually complete aTask<T>with a result, exception, or cancellation. Use it to adapt callback- or event-based APIs to the task-based asynchronous pattern.
Detailed answer
For example, an API may signal completion through an event rather than return a task. A TaskCompletionSource<T> can complete when that event arrives, allowing callers to await it. Use TrySetResult, TrySetException, or TrySetCanceled when multiple paths may race. Prefer existing task-based APIs when available, and consider asynchronous continuations to avoid running user continuations unexpectedly on the completion thread.
For example, an adapter around a callback-only API can complete a TaskCompletionSource<Result> from its success callback and complete it with an exception from its error callback, allowing callers to use await instead of registering their own callbacks.
Sources
Memory
Garbage Collector and Heaps
Question
Explain the garbage collector, small object heap, and large object heap.
Short interview answer
The .NET garbage collector reclaims managed objects that are unreachable from application roots. It is generational: short-lived objects begin in generation 0, while long-lived objects advance to older generations. Large allocations use the large object heap and are collected with generation 2.
Detailed answer
The GC starts from roots such as active stack references, static fields, and handles, then follows references. Objects it cannot reach are eligible for collection. For example, after var report = new Report(); leaves scope, the object can be collected only if nothing else—such as a cache or event subscription—still refers to it. That is why “the variable went out of scope” is not a release guarantee.
Most allocations are ordinary small-object allocations. An allocation of 85,000 bytes or more goes to the large object heap (LOH). A byte[100_000] is therefore a LOH allocation; repeatedly allocating such buffers for request processing can force more expensive collections and fragment memory. The answer is not to call GC.Collect(): reuse or pool buffers only after measurement, and first remove unnecessary retention. The GC manages memory; it does not close files, return database connections, or release native handles promptly.
Sources
IDisposable vs. Finalizer
Question
Describe the difference between
IDisposableand a finalizer.
Short interview answer
Disposeprovides deterministic cleanup initiated by the consumer. A finalizer is a nondeterministic GC fallback for directly owned unmanaged resources. A finalizer should not repeat allDisposework because managed objects may already be finalized or unavailable.
Detailed answer
Dispose is an explicit ownership boundary. When the caller finishes with a FileStream, a database connection, or a native handle wrapper, it calls Dispose—usually through using—and the resource is released at that point. A finalizer is different: the garbage collector schedules it only after the object becomes unreachable, so there is no predictable time at which it will run.
Consider a type that directly owns a native buffer and also owns a managed diagnostic stream:
sealed class NativeImage : IDisposable
{
private IntPtr _buffer; // Allocated by a native API.
private readonly Stream _trace; // A managed dependency.
public void Dispose()
{
_trace.Dispose(); // Safe: the consumer controls the timing.
FreeNativeBuffer();
GC.SuppressFinalize(this); // No finalizer pass is now needed.
}
~NativeImage()
{
FreeNativeBuffer(); // Fallback only: do not use _trace here.
}
private void FreeNativeBuffer()
{
if (_buffer == IntPtr.Zero) return;
NativeApi.Free(_buffer);
_buffer = IntPtr.Zero;
}
}
using var image = new NativeImage(...); calls Dispose when execution leaves the scope, including when an exception is thrown. If the caller forgets, the finalizer can eventually free the directly owned native buffer, but it must not rely on _trace: that managed object may already have been finalized. Prefer SafeHandle for unmanaged handle ownership where possible; it avoids many reasons to write a finalizer yourself.
Sources
using Statement
Question
Describe a
usingblock. How does it work underneath?
Short interview answer
A
usingstatement ensures that anIDisposableobject is disposed when control leaves its scope. Conceptually, it compiles to atry/finallythat callsDispose.
Detailed answer
Use using when the current scope owns a disposable resource:
using var stream = File.OpenRead(path);
var document = JsonDocument.Parse(stream);
When execution leaves the scope—whether normally or because Parse throws—the compiler-generated cleanup path calls stream.Dispose(). Conceptually it is a try/finally; it is not a hint to the garbage collector. That matters for files, database connections, and native handles, where waiting for eventual collection can exhaust a scarce resource. Use await using when the resource implements IAsyncDisposable and cleanup itself is asynchronous.
Sources
Lazy Initialization
Question
Describe
Lazy<T>.
Short interview answer
Lazy<T>delays creation of a value until itsValueproperty is first accessed. It is useful when construction is expensive or may never be needed.
Detailed answer
Lazy<T> stores a factory and does not call it until Value is requested. For example, new Lazy<Regex>(() => new Regex(pattern)) avoids constructing a costly regular expression during application startup if a user might never open the feature that needs it. Once Value is requested, the result is retained and returned on later requests.
Decide whether multiple threads can reach Value at the same time and choose the documented thread-safety mode accordingly. Lazy initialization trades startup work for first-use latency and moves constructor failures to the first caller. It is a good fit for optional expensive state, not a default replacement for simple eager construction.
Sources
Memory Leaks in .NET
Question
Is a memory leak possible in .NET? Provide examples.
Short interview answer
Yes. The GC reclaims only unreachable managed objects, so objects unintentionally kept reachable leak memory from the application's perspective. Common causes include event subscriptions, static caches, and unbounded collections.
Detailed answer
A managed leak means an object remains reachable even though the application no longer needs it. For example, a screen subscribes to a long-lived event source and is then closed:
publisher.Updated += screen.Refresh;
The publisher now holds the delegate, which holds the screen. The screen cannot be collected until it unsubscribes or the publisher dies. The same problem appears in unbounded static dictionaries, caches with no eviction, queued callbacks, and service singletons that retain request-specific objects. A separate class of leak is an undisposed native resource, such as a file or socket handle.
Diagnose the repeated scenario with allocation and heap tooling, then inspect the retaining reference path for an object that should have disappeared. Forcing a collection only hides timing; remove the unwanted owner, bound the cache, or dispose the resource.
Sources
WeakReference
Question
Describe
WeakReferenceand when it could be useful.
Short interview answer
A
WeakReferencerefers to an object without keeping it alive for garbage collection. It can be useful for recreatable caches, but callers must handle the target being collected at any time.
Detailed answer
A WeakReference<T> lets the garbage collector reclaim its target even while the weak reference exists. It is suitable for a recreatable cache, not for an object the application requires to remain alive:
if (!previewCache.TryGetTarget(out var preview))
{
preview = CreatePreview();
previewCache.SetTarget(preview);
}
Render(preview);
The cache may contain a preview on one call and no target on the next; the code must handle both outcomes. Once preview is assigned to a local variable, that local provides the strong reference needed during rendering. Weak references do not fix accidental ownership such as an event subscription or an unbounded cache. Find and remove the unwanted strong reference first.
Sources
Finalizer Cleanup Scope
Question
Can a finalizer execute exactly the same logic as
Dispose?
Short interview answer
No. A finalizer should release only unmanaged resources directly owned by the type. It must not depend on other managed objects because their finalization order is not guaranteed.
Detailed answer
The dispose pattern separates cleanup performed by Dispose from finalizer cleanup. Dispose can clean managed and unmanaged resources because the caller controls timing. A finalizer is a GC fallback and should avoid managed dependencies; otherwise it can run after those dependencies have already been finalized. For example, a type that directly owns a native buffer may free that buffer from its finalizer, but it must not flush a managed Stream, notify a managed service, or call back into arbitrary application code there.
Sources
Reflection
Reflection
Question
Define reflection and provide examples of its possibilities. Where can it be useful, and what are its downsides?
Short interview answer
Reflection inspects assemblies, types, and members at runtime and can create instances or invoke members dynamically. It is useful for frameworks, metadata-driven tooling, serialization, and plugins, but loses compile-time safety and can add complexity and overhead.
Detailed answer
System.Reflection exposes types such as Assembly, Type, and MethodInfo. A serializer may discover annotated properties; a plugin host may load a type by convention. Prefer normal static calls when the type is known: reflection is harder to refactor safely, may be slower, and can conflict with trimming or ahead-of-time compilation unless dependencies are declared.
Sources
dynamic Type
Question
Describe the
dynamictype. How does it differ from an anonymous type, and can one dynamic variable hold different data types?
Short interview answer
dynamicdefers member binding and type checks to runtime. An anonymous type is compiler-generated, strongly typed, and has a fixed shape. Adynamicvariable can be assigned values of different runtime types, but invalid operations fail at runtime.
Detailed answer
dynamic is useful mainly for COM interop, dynamic-language integration, or genuinely dynamic object models. The compiler does not validate the member call, so a missing member or wrong arguments produce a runtime binder failure. An anonymous type is not dynamic: its members are inferred at compile time and cannot be changed after creation.
Sources
ExpandoObject
Question
Describe
ExpandoObject.
Short interview answer
ExpandoObjectis a dynamic object whose members can be added and removed at runtime. It is useful for flexible object shapes, but gives up compile-time guarantees.
Detailed answer
An ExpandoObject can be accessed through dynamic or as a dictionary of member names and values. It fits integrations or data whose schema is genuinely open-ended. Prefer a defined class or record when the shape is known, because static types give refactoring support, validation, and clearer contracts.
Sources
Attributes
Question
Explain attributes. What data can be passed into them, and where is that information stored?
Short interview answer
Attributes attach declarative metadata to assemblies and program elements. Constructor arguments and named public fields or properties use a restricted set of compile-time metadata values. The metadata is stored in the assembly and can be read through reflection.
Detailed answer
Attributes commonly express serialization, interop, test, or framework behavior. An attribute declaration is metadata, not automatically executed behavior; a compiler, runtime, or framework must read and act on it. Allowed argument values are limited by the language specification, including primitive values, strings, Type, enums, and arrays of allowed types.
Sources
Reflection Trade-offs
Question
Are there any downsides to reflection?
Short interview answer
Reflection reduces compile-time checking, can add runtime overhead, complicates refactoring, and may require extra configuration for trimming or ahead-of-time compilation.
Detailed answer
Reflection is appropriate when metadata-driven behavior is genuinely needed, but ordinary static calls are safer and clearer when types are known. Invoking members by name moves errors from compilation to runtime. Reflection-heavy code can also be harder for tools to analyze, so deployment modes that remove unused code need explicit metadata-preservation rules.
Sources
Reflection Use Cases
Question
Where can reflection be useful?
Short interview answer
Reflection is useful for frameworks and tools that inspect metadata at runtime, such as serializers, dependency-injection containers, test runners, plugin loaders, and type browsers.
Detailed answer
The common pattern is to discover types or attributes and then apply a convention. For example, a plugin host can find implementations of an interface in an assembly; a serializer can inspect public properties or annotations. Keep reflection at the boundary of such infrastructure instead of spreading string-based member access through application code.
Sources
Anonymous Types vs. dynamic
Question
Explain the difference between anonymous and
dynamictypes.
Short interview answer
An anonymous type has a compiler-generated, fixed shape checked at compile time.
dynamicdefers member binding to runtime and may fail only when an operation executes.
Detailed answer
Anonymous types are useful for local projections, such as a LINQ select new { Name, Total }. The compiler knows their members within the scope where the inferred type is available. dynamic is for runtime-discovered members and should be limited to interoperability or truly dynamic data.
Sources
Values Held by dynamic
Question
Can a single variable of a
dynamictype store different types of data?
Short interview answer
Yes. A
dynamicvariable can be assigned values with different runtime types. The member operations performed on it are resolved at runtime.
Detailed answer
For example, a variable may first hold a string and later an object returned by a COM API. The compiler permits calls without validating the target's members; invalid calls fail at runtime. That flexibility is why dynamic should be constrained to boundaries where the shape cannot be represented statically.
Sources
Attribute Arguments
Question
What kind of data can be passed into attributes?
Short interview answer
Attribute constructor and named arguments are restricted to metadata-compatible values, including primitive values, strings,
Type, enums, and arrays of permitted types.
Detailed answer
Attribute arguments are encoded as metadata rather than arbitrary runtime objects. Positional arguments correspond to constructor parameters; named arguments set public fields or properties. This restriction lets tools inspect attributes without executing arbitrary application code.
Sources
Attribute Metadata Storage
Question
Where is information set in attributes stored?
Short interview answer
Attribute information is stored as metadata in the compiled assembly and can be retrieved through reflection.
Detailed answer
Attributes decorate assemblies, types, members, parameters, and return values. The compiler writes their constructor and named argument data into assembly metadata. Reflection APIs can read that metadata at runtime; some tools also inspect it without running the application.
Sources
Patterns and Architecture
SOLID Design Principles
Question
Explain SOLID.
Short interview answer
SOLID is a set of five object-oriented design principles: single responsibility, open/closed, Liskov substitution, interface segregation, and dependency inversion. Together they help keep dependencies and responsibilities small enough to change and test independently.
Detailed answer
SOLID is not a framework or a required class hierarchy. It is a way to evaluate whether a design makes change unnecessarily risky. For example, an order service that both calculates prices and sends email has two independent reasons to change; separating notification behind an interface gives it one focused responsibility. A caller that needs only IOrderReader should not be forced to depend on write operations, and application code should depend on an abstraction such as INotificationSender rather than directly constructing a concrete email client. The concrete email client can then be supplied by dependency injection. The result is not “more interfaces everywhere”; it is a design where meaningful responsibilities and dependencies are explicit.
Glossary
- SOLID: Single responsibility, open/closed, Liskov substitution, interface segregation, and dependency inversion.
Sources
Test-Driven Development
Question
Explain TDD, have you used it?
Short interview answer
Test-driven development (TDD) means writing a failing automated test for the next required behavior, implementing the smallest change that makes it pass, then improving the code while keeping the tests passing.
Detailed answer
TDD uses the test as an executable statement of behavior. Suppose a pricing rule must reject a negative quantity: first add a test that expects that result, run it and observe failure, then implement only the validation needed to pass. Refactor afterwards if the code or test can be clearer. This loop is useful when it leads to fast, isolated tests and a better API, but it does not replace integration testing, exploratory testing, or production monitoring. A test suite is valuable when it checks observable behavior rather than merely mirroring implementation details.
Glossary
- TDD: Test-driven development.
Sources
Domain-Driven Design
Question
Explain Domain Driven Design.
Short interview answer
Domain-driven design (DDD) models complex business behavior with the people who understand that domain, then protects that model from infrastructure details. It is most useful where business rules are genuinely complex, not for every simple create/read/update/delete application.
Detailed answer
DDD starts with a shared language between developers and domain experts. In a trading system, “settlement”, “trade”, and “position” should have precise business meanings in the model rather than being vague database labels. A bounded context limits where a particular meaning applies, while entities, value objects, aggregates, and domain events express rules and changes inside that context. The approach pays off when rules evolve and carry significant business meaning. For a small reporting or simple CRUD application, its modeling and coordination cost may outweigh the benefit; a focused hybrid can use DDD only for the complex transactional area.
Glossary
- CRUD: Create, read, update, and delete.
- DDD: Domain-driven design.
Sources
Tests
Unit Tests vs. Integration Tests
Question
Describe differences between unit and integration tests.
Short interview answer
A unit test exercises a small unit of code under the developer’s control, usually without infrastructure. An integration test checks whether multiple components work together and can include real infrastructure boundaries.
Detailed answer
A unit test for PriceCalculator can construct the calculator with predictable inputs and assert its result without a database or HTTP service. That makes it quick and pinpointed when it fails. An integration test can instead verify that an API endpoint, its serialization, and a test database work together. Neither replaces the other: unit tests give rapid feedback about local behavior, while integration tests expose wiring and boundary problems that isolated tests deliberately do not cover. The appropriate balance depends on the risk of the feature and the cost of each test environment.
Sources
Unit-Test Isolation
Question
Should you unit test in isolation?
Short interview answer
Usually yes: a unit test should isolate the unit of work from external systems so it is fast, repeatable, and identifies the behavior that failed. Tests that intentionally exercise integrations belong in the integration-test layer.
Detailed answer
Isolation does not mean testing private implementation details or pretending collaborators do not exist. It means controlling dependencies that would make the result variable or slow. For example, inject a clock into expiry logic so the test can use a fixed instant, and replace a network gateway with a test double when the unit’s behavior—not the gateway—is under test. Keep a separate integration test to prove the real gateway configuration works. This division gives meaningful failures: a unit-test failure points to local behavior, while an integration-test failure can reveal an issue at a real boundary.
Sources
Testing in Isolation
Question
How can you unit test in isolation?
Short interview answer
Design the unit around explicit dependencies, pass controlled test doubles for external collaborators, and keep time, I/O, and random inputs deterministic. Assert observable results or interactions that are part of the unit’s contract.
Detailed answer
Start by separating business logic from infrastructure through constructor parameters or other explicit dependencies. For example, a service that needs the current time can depend on an interface whose test implementation always returns 2026-08-29T10:00:00Z; the production implementation can read the real clock. Use fakes, stubs, or mocks only at boundaries such as a database, file system, HTTP client, clock, or message publisher. Do not mock every class: a test that only checks a chain of mocks can pass even when the real behavior is wrong. A good isolated test stays fast, repeatable, self-checking, and focused on one behavior.
Sources
Version Control
Git as Version Control
Question
What is
git?
Short interview answer
Git is a distributed version control system that records a project’s history as commits and lets developers work in local branches, then exchange and integrate changes with other repositories.
Detailed answer
Git records snapshots of the project as commits connected in a history. A branch is a movable reference to a line of commits, so a developer can create a feature branch, commit work locally, and later share it with a remote repository. Because the repository is distributed, a clone is not merely a working copy: it contains history and can create commits without a central server being available. Collaboration still needs an agreed workflow for review, integration, and protecting shared branches.
Sources
Centralized vs. Distributed Version Control
Question
Difference between
centralizedanddecentralizedVSC, which one isgit?
Short interview answer
A centralized version control system keeps the authoritative history on a central server and clients typically need that server for history operations. A distributed system gives each clone a full repository history; Git is distributed.
Detailed answer
In a centralized workflow, the server is the primary location for history and collaboration. In a distributed workflow, each developer’s clone has the commit graph and can create branches, inspect history, and commit locally. Git repositories can still designate a shared remote such as origin, and most teams do so for coordination and backup, but the model does not require a server for every operation. “Distributed” therefore describes Git’s repository structure, not the absence of a shared team workflow.
Glossary
- VCS: Version control system.
Sources
Merge vs. Rebase
Question
Difference between
mergeandrebase.
Short interview answer
Merge combines histories and normally creates a merge commit when the histories have diverged. Rebase replays a branch’s commits onto another base, creating new commits and therefore rewriting that branch’s history.
Detailed answer
Suppose main receives commit M while a feature branch has commits F1 and F2. A merge can connect both lines of history with a merge commit. A rebase can replay the feature changes on top of M, producing new commits equivalent in content to F1 and F2 but with new identities. Rebasing can make a local, unshared feature history linear; merging preserves the existing topology. Do not casually rebase commits that other people are already basing work on, because their history no longer shares the same commit identities.
Sources
Branches
Question
Explain branches in git or in general VSC.
Short interview answer
In Git, a branch is a lightweight movable reference to a commit. It represents an independent line of development that can later be merged, rebased, or discarded without changing another branch.
Detailed answer
Creating feature/invoice-export from main gives the feature a starting commit. New commits advance that branch while main can continue to advance separately. The branch name itself is not a copy of every file; it is a reference into the commit graph. When the feature is ready, the team can review its changes against main, resolve conflicts if both lines changed the same area, and integrate it according to its chosen workflow.
Glossary
- VCS: Version control system.
Sources
Merge Conflicts
Question
Can there be any problems while merging changes with other developers?
Short interview answer
Yes. A conflict occurs when Git cannot automatically combine overlapping changes. Developers must inspect the intended result, edit it, test it, mark the conflict resolved, and then complete or abort the merge or rebase.
Detailed answer
If two developers change the same lines of InvoiceCalculator, Git may not know which version represents the required behavior. Conflict markers show the competing changes, but they are not a solution: someone who understands the feature must produce the correct combined code. Build and test after resolution, because a textually resolved conflict can still be behaviorally wrong. Small focused commits, early communication about overlapping work, and integrating upstream changes regularly reduce the size and surprise of conflicts; they cannot eliminate genuine semantic disagreements.
Sources
Rebasing Long-Running Work
Question
If you're working on some long task, when would you try to rebase on main branch? How often would you do that?
Short interview answer
Rebase an unshared feature branch onto the current main branch when updated main-line changes affect the work or before integration, at a cadence that keeps conflicts manageable. Avoid rewriting commits that collaborators already depend on unless the team has explicitly agreed on that workflow.
Detailed answer
There is no universal interval such as “every day.” For a multi-week feature, integrate current main often enough that conflicts are still understandable—especially after changes to shared APIs, build configuration, or nearby code. Rebase is suitable when the branch is private or the team agrees that it may be force-updated. If the branch is shared, merge main into it instead, or coordinate the rewrite, because rebase creates new commit identities. Before opening a pull request, update from main, resolve and test conflicts, then make the integration state visible to reviewers.
Sources
Code Reviews
Question
Did you perform code reviews? How did that look like?
Short interview answer
A useful code review evaluates a focused proposed change before it is merged: reviewers check correctness, tests, security and maintainability risks, ask clear questions, and record an approval or requested changes. The exact workflow is a team practice, not a Git feature by itself.
Detailed answer
In a pull-request workflow, the author explains the goal, scope, and how the change was tested. Reviewers inspect the diff in manageable commits, compare it with the requirement, and leave specific comments or suggestions. The author responds, updates the branch, and the review is repeated until the change meets the team’s standards. Keep review feedback about the code and its impact, not the author; small pull requests are easier to understand and review than a large mixed change. Platforms can require approval before merge, but that control is most effective alongside shared review expectations.
Sources
Mobile
Xamarin
Shared UI vs. Native UI
Question
Differences between
Xamarin.FormsandXamarin Nativeand when to use which?
Short interview answer
Xamarin.Forms shared a UI layer across platforms; Xamarin.Native used platform-specific Xamarin.Android and Xamarin.iOS UI APIs. Choose shared UI for common screens and native UI when platform-specific behavior, design, or controls dominate. For new cross-platform work, evaluate .NET MAUI rather than Xamarin.Forms.
Detailed answer
The distinction is primarily where UI code lives. A shared UI framework lets one page definition render native controls on each platform, which can reduce duplicated work for conventional screens. Native UI projects expose Android and iOS controls directly, giving more control over lifecycle, navigation, performance tuning, and platform conventions. A real application can share domain and network code while keeping selected platform-specific screens. Xamarin.Forms is historical; Microsoft describes .NET MAUI as its evolution, so a new project should base its decision on MAUI and the current native toolchains.
Glossary
- UI: User interface.
Sources
MVVM
Question
MVVM- what it is (layers) and how to use it in native project (popular frameworks - bindings, converters etc.).
Short interview answer
Model-View-ViewModel (MVVM) separates UI state and presentation behavior from the view. The view binds to a view model; the model represents domain or data concerns. In a native app, the same separation can be implemented with platform-specific binding or observation mechanisms.
Detailed answer
The view model exposes display-ready state and commands, while the view renders it and forwards user actions. For a login screen, the view model can expose Email, Password, validation state, and a sign-in command; the platform view handles layout and native control behavior. Value converters can translate a view-model value into a display-specific value, but business rules should remain in the model or view model. MVVM is a structure, not a requirement to use a particular framework: avoid placing navigation, lifecycle, or platform APIs into a supposedly portable view model when they are truly platform concerns.
Glossary
- MVVM: Model-View-ViewModel.
Sources
Emulator vs. Simulator
Question
Explain difference between
emulatorandsimulator.
Short interview answer
An emulator reproduces a target device environment closely enough to run target-platform software; a simulator reproduces selected behavior without necessarily reproducing the target hardware or full operating environment. In practice, use the platform documentation for the exact tool’s limits and test important behavior on real devices.
Detailed answer
The labels are less important than the fidelity of the test. Device-specific behavior such as sensors, graphics performance, battery use, notifications, hardware-backed security, network variability, and OS integration can differ from desktop-hosted tools. Use a virtual device to accelerate development and automate repeatable scenarios, then validate release-critical flows on representative physical devices. Treat an emulator or simulator result as evidence for the environment it models, not proof that every real-device condition is correct.
Sources
Reducing App Size
Question
How to decrease Xamarin app size?
Short interview answer
Measure the release package first, remove unused dependencies and resources, ship only required native architectures, and enable the platform’s release-time linking or code shrinking with tests that cover reflection and dynamically discovered code.
Detailed answer
An application package includes the app, dependencies, runtime pieces, resources, and native binaries. The safest reduction usually comes from deleting unused features and large assets, then reviewing transitive dependencies rather than enabling an aggressive linker blindly. Xamarin’s linker can remove code that is not directly used, but reflection or dynamically discovered members can be removed unless they are preserved explicitly. Verify the release build on target devices after each size optimization; a smaller package that fails only in a rare screen is not an improvement.
Sources
Linker Problems
Question
Possible problems with
linkerand how to resolve it?
Short interview answer
A linker can remove code that is not statically visible as used, breaking reflection, dependency injection, serialization, or platform callbacks at runtime. Preserve the required members through supported configuration or annotations, and test the actual release build.
Detailed answer
Code elimination is based on what the build can determine. If an app loads a type by name or invokes a member through reflection, the linker may not see the dependency. The symptom is often a release-only failure such as a missing type, missing member, or a feature that silently stops working. Diagnose the exact dynamically reached code path, preserve only the required types or members, and add a release-build test for it. Turning linking off can be a temporary diagnostic step, but it gives up size benefits and does not explain the dependency to future maintainers.
Sources
Investigating Mobile Memory Problems
Question
Memory problems - how to investigate?
Short interview answer
Reproduce the issue in a release-like build, measure allocations and retained objects with platform profiling tools, then trace why an object remains reachable. Fix the ownership or lifecycle issue and verify memory returns to a stable baseline through repeated use.
Detailed answer
Start with a repeatable scenario such as opening and closing a media-heavy screen fifty times. Compare memory before, during, and after the loop; a temporary rise is not by itself a leak, but continually retained screen, image, subscription, or cache objects are evidence to investigate. Check event subscriptions, navigation stacks, long-lived services, static references, and native-resource disposal. Use a profiler’s allocation and reference information to identify the retaining path, then test the fix on real devices because memory pressure and process termination are platform behavior, not just managed-runtime behavior.
Sources
Multiple Cell Types in a List
Question
Can a list contain different types of cell? How to implement it?
Short interview answer
Yes. Model each row’s kind explicitly, choose the corresponding item view or template from that kind, and bind it to the matching data. Keep view selection separate from the list’s underlying data.
Detailed answer
A feed may contain an article row, an advertisement row, and a loading row. Give each item a declared row kind, then have the adapter or template selector create and bind the appropriate view holder or view template. In Android’s RecyclerView model, the adapter owns the mapping between item data and view holders, while the layout manager arranges them. Avoid inferring the cell type from presentation text; an explicit data model makes updates, tests, and accessibility behavior easier to maintain.
Sources
Native Library Bindings
Question
Is it possible to use Java library in Xamarin.Android and Objective-C/Swift libraries in Xamarin.iOS?
Short interview answer
Yes, but through bindings or interoperable APIs rather than by directly treating Java, Objective-C, or Swift source as C#. A binding exposes the native library’s supported API to managed code and introduces versioning, ownership, and platform-toolchain constraints.
Detailed answer
For Xamarin.Android, a binding library can wrap a Java or Kotlin library so C# calls cross the Java Native Interface boundary. The binding must include compatible Java and native dependencies, and its target API level must be compatible with the library. On iOS, use a supported .NET iOS binding or write one for an Objective-C-compatible API; Swift libraries often need an Objective-C-compatible surface before binding. Treat the binding as an integration boundary: validate initialization, threading, callbacks, memory ownership, and updates against the actual devices.
Glossary
- API: Application programming interface.
- JNI: Java Native Interface.
Sources
Choosing Mobile Layouts
Question
What layout elements would you use to implement such view (show some design)?
Short interview answer
Start from the screen’s constraints and interaction hierarchy, then choose the smallest layout structure that expresses them. Use a scrolling container for content that can exceed the viewport, reusable list components for repeated data, and platform layout constraints for alignment and adaptation.
Detailed answer
For a product list, use a screen-level container, a top app bar, and a RecyclerView for the repeated product rows rather than manually placing every row. A row can use a constraint-based layout when its image, title, price, and action need relationships that adapt across widths. For a settings screen, a vertically scrolling group of distinct controls can be more appropriate than a list. The point is not to name a fashionable layout: explain which elements repeat, which must scroll, which dimensions are flexible, and how the design remains usable on small and large screens.
Sources
Android
ListView vs. RecyclerView
Question
ListViewvsRecyclerView
Short interview answer
RecyclerView is the modern flexible list container: it uses adapters, view holders, and a layout manager to efficiently display and recycle items. ListView is an older, simpler list widget.
Detailed answer
RecyclerView separates item binding from item layout:
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetAdapter(new ProductAdapter(products, OnProductSelected));
The adapter creates and binds view holders, while the layout manager chooses list, grid, or custom positioning. Reusing off-screen views improves responsiveness and reduces power use for large data sets. Prefer RecyclerView for new View-based Android lists.
Sources
Android Layout Types
Question
Layouts types on android
Short interview answer
Android View layouts are ViewGroup containers that position child views. Select the container that expresses the screen’s relationships without unnecessary nesting; common choices include linear, frame, and constraint-based layouts.
Detailed answer
A vertical form may use a linear arrangement; a layered image and badge can fit a frame arrangement; a responsive screen with relationships among multiple elements suits ConstraintLayout:
<androidx.constraintlayout.widget.ConstraintLayout ...>
<ImageView android:id="@+id/avatar" ... />
<TextView app:layout_constraintStart_toEndOf="@id/avatar" ... />
</androidx.constraintlayout.widget.ConstraintLayout>
A repeated collection is normally RecyclerView, not a giant static layout. Choose from the layout behavior required by the screen, then inspect the resulting hierarchy.
Sources
ConstraintLayout Tools
Question
ConstraintLayout- guidelines vs barriers, groups
Short interview answer
A guideline is a fixed or proportional invisible reference line; a barrier positions itself from the edge of referenced views; a group controls the visibility of several views together. They describe different kinds of layout relationships.
Detailed answer
Use a guideline when several elements share a design alignment, a barrier when a control must begin after whichever referenced label is widest, and a group for views that appear or disappear together:
<androidx.constraintlayout.widget.Guideline
android:id="@+id/contentGuideline"
app:layout_constraintGuide_percent="0.32" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/labelEnd"
app:barrierDirection="end"
app:constraint_referenced_ids="title,subtitle" />
These helpers keep the relationship declarative instead of calculating positions in code.
Sources
MultiDex
Question
MultiDex
Short interview answer
MultiDex lets an Android app use multiple DEX files when it exceeds a single DEX file’s 65,536-reference limit. It matters primarily for apps supporting Android versions before API 21; newer runtimes support multiple DEX files natively.
Detailed answer
First reduce unnecessary dependencies and enable release code shrinking. If the minimum SDK is 20 or lower and the limit remains, configure multidex in the release build:
android {
defaultConfig { multiDexEnabled = true }
}
dependencies { implementation("androidx.multidex:multidex:2.0.1") }
For API 21 and later, the runtime supports multiple DEX files natively. This legacy compatibility configuration can increase startup and build costs, so it is not a substitute for dependency discipline.
Glossary
- DEX: Dalvik Executable.
Sources
Activity vs. Fragment
Question
Activityvsfragment
Short interview answer
An activity is an Android entry point for user interaction and participates directly in the system lifecycle. A fragment is reusable UI behavior hosted by an activity or another fragment and has its own lifecycle, including a separate view lifecycle.
Detailed answer
An activity is a system entry point; a fragment is hosted UI that must be managed through the host’s FragmentManager:
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content, new InvoiceFragment())
.AddToBackStack(null)
.Commit();
Do not treat a fragment as a miniature activity: its view can be destroyed while the fragment object remains, so clear view references at the appropriate lifecycle boundary.
Sources
Accessing a RecyclerView Item
Question
How to get access to single item in
recycler viewfrom code?
Short interview answer
Keep the item in the adapter’s data model and expose an explicit callback or selection event from its view holder. Use the adapter position only at the time of interaction, then look up the corresponding model item.
Detailed answer
The view is recyclable and should not become the source of truth. Bind the model identifier and report it through a callback:
holder.ItemView.Click += (_, _) =>
{
var position = holder.BindingAdapterPosition;
if (position != RecyclerView.NoPosition)
onItemSelected(items[position].Id);
};
The screen acts on the model identifier, not on the row view. Do not retain a row view or assume a displayed position stays valid after list updates.
Sources
Battery Status
Question
You need to get info about
battery statusin the device? What mechanism can you use?
Short interview answer
Use a
BroadcastReceiverwhen the app must react to power-state changes. Register forACTION_POWER_CONNECTEDandACTION_POWER_DISCONNECTED; the receiver can adjust or reschedule work when charging begins or ends.
Detailed answer
For example, a Xamarin.Android receiver can change an app’s background-update policy when the device is plugged in or unplugged:
[BroadcastReceiver(Enabled = true, Exported = false)]
[IntentFilter(new[] { Intent.ActionPowerConnected, Intent.ActionPowerDisconnected })]
public sealed class PowerConnectionReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var isCharging = intent.Action == Intent.ActionPowerConnected;
var state = isCharging ? "charging" : "not charging";
Android.Util.Log.Info("PowerConnection", state);
}
}
The Log call only makes the received state observable in this example; it is not a proposed application feature. The point is that OnReceive gets a real power-change event. For actual deferrable work such as a large upload, do not build a receiver that manually tracks charging state: declare WorkManager’s RequiresCharging constraint and let Android run the worker only while charging. For a one-off current level, query the sticky ACTION_BATTERY_CHANGED intent and read its level and scale extras instead.
Sources
Layout Inspector
Question
Which tool would you use to see why your views are layouted incorrectly.
Short interview answer
Use Android Studio’s Layout Inspector. It shows a running app’s component hierarchy and attributes on an emulator or physical device.
Detailed answer
Inspect the selected view’s constraints, bounds, parent hierarchy, and attributes, then compare the rendered result with the intended design. This reveals issues such as an unexpected parent constraint or a view with zero size more reliably than guessing from XML alone.
Sources
ProGuard and R8
Question
ProGuard
Short interview answer
ProGuard rules are now commonly consumed by R8, Android’s shrinker and optimizer. R8 can remove unused code, optimize code, and shorten identifiers; code reached through reflection needs explicit keep rules.
Detailed answer
Enable shrinking for release builds incrementally and verify affected flows. A reflective serializer may need a narrowly scoped keep rule:
-keep class com.example.api.dto.** { *; }
A rule that is too broad loses size benefits; a missing rule can break serialization, reflection, or native callbacks. Keep rules with the dependency or feature that requires them.
Sources
Android Support Library
Question
Support Library
Short interview answer
The Android Support Library was a compatibility library family. Its
android.supportartifacts are deprecated; AndroidX/Jetpack is the current replacement for new work.
Detailed answer
The historical libraries made newer APIs and behaviors available across older platform versions. A migration changes the package family, for example:
android.support.v7.widget.RecyclerView
→ androidx.recyclerview.widget.RecyclerView
Modern projects should use AndroidX, which replaced the old package namespace and continues to receive development. Do not mix old Support Library and AndroidX dependencies.
Sources
The merge Layout Tag
Question
When to use merge
Short interview answer
Use
<merge>as a reusable layout’s root when its children should be inserted directly into an existing compatible parent, avoiding an otherwise redundant ViewGroup.
Detailed answer
A reusable pair of controls included inside an existing vertical parent does not need another vertical container merely to exist:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button android:layout_width="match_parent" android:layout_height="wrap_content" />
<Button android:layout_width="match_parent" android:layout_height="wrap_content" />
</merge>
<merge> inserts these children directly into the including parent and removes a redundant wrapper. It is appropriate only when that parent supplies the necessary layout context.
Sources
The include Layout Tag
Question
When to use include
Short interview answer
Use
<include>to reuse a complete layout resource in several layouts. It centralizes a common UI component while allowing supported root layout attributes to be overridden at the inclusion point.
Detailed answer
Extract a shared title bar or empty-state component into its own layout and include it where required:
<include
layout="@layout/empty_state"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
This avoids copied XML. When overriding layout attributes on an included root, specify both width and height as required by the platform rules.
Sources
iOS
Provisioning Profiles
Question
What is
Provisioning Profile?
Short interview answer
A provisioning profile ties an app identifier, signing certificate, and permitted devices or distribution method together so iOS can validate how an app is signed and installed.
Detailed answer
Development, ad hoc, and distribution workflows use different permissions. Treat profiles and certificates as deployment credentials: keep them managed, rotate them when required, and make the build configuration match the intended destination.
Sources
Auto Layout
Question
What is
AutoLayout?
Short interview answer
Auto Layout is UIKit’s constraint-based system for determining view size and position in response to the view hierarchy and environment.
Detailed answer
Define relationships rather than manually assigning every frame:
title.LeadingAnchor.ConstraintEqualTo(View.LayoutMarginsGuide.LeadingAnchor).Active = true;
title.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor, 16).Active = true;
Constraints can adapt across device sizes and orientation changes; conflicting or incomplete constraints must be resolved deliberately.
Sources
frame vs. bounds
Question
What’s the difference between the
frameand thebounds?
Short interview answer
A view’s frame describes its size and location in its superview’s coordinate system. Its bounds describe its size and internal coordinate system.
Detailed answer
For a 100-by-40 view placed at x=20, y=30 in its parent, Frame is (20, 30, 100, 40) while Bounds is normally (0, 0, 100, 40): the former uses the parent’s coordinates, the latter uses the view’s own coordinates. Use Auto Layout for normal adaptive positioning. Frame is not reliable with a non-identity transform; UIKit documents using center and bounds instead in that case.
Sources
Xamarin.iOS Reflection Limits
Question
Is there any limitation in
reflectionin Xamarin.iOS? Emit, access to non-public api.
Short interview answer
iOS ahead-of-time compilation and platform policy constrain dynamic techniques. Do not depend on runtime code generation or private platform APIs; preserve only reflection metadata that a supported release feature needs.
Detailed answer
Treat dynamic invocation as a deployment concern, not just a language feature. Validate the release build on device, avoid private Apple APIs, and use supported bindings or generated code where possible. A library that relies on reflection needs explicit trimming/AOT review.
Sources
Size Classes
Question
Size classes
Short interview answer
Size classes describe broad horizontal and vertical space categories. Interface Builder and UIKit use them to apply layouts and constraints appropriate to the current environment.
Detailed answer
Start with a general Any-Any layout, then add more-specific variants only where the design needs them. Rotation, split view, and device changes can alter the active size class, so verify every supported combination has a satisfiable layout.
Sources
Interface Builder Outlets
Question
How to get access to view/controls created in Interface Builder in c#?
Short interview answer
Connect the Interface Builder control to an outlet on the corresponding view controller or view class; the binding generates a C# member that is populated when the storyboard or nib is loaded.
Detailed answer
The outlet belongs to the object that owns the scene and is populated when Interface Builder loads it:
partial class ProfileViewController : UIViewController
{
[Outlet] UILabel NameLabel { get; set; } = null!;
}
Give it a meaningful name, connect it in Interface Builder, and access it after the view has loaded. The outlet represents a UI element, not application state; keep durable state in the model or view model.