Infosys β€” Interview Master Guideone section at a time Β· ← β†’ move Β· Space=pause Β· M=mark for review
Left β€” Goal h

I01 β€” Core CS & Algorithmic Rigor (DSA & System Logic)

🎯 Why this matters for Infosys: the online round (HackerRank / InfyTQ) is pattern-recognition under time pressure β€” one clever math observation often replaces a whole loop. The interview round is narration: they want to hear brute force β†’ why it's slow β†’ the optimisation β†’ the complexity, out loud, before you type. Every subsection here arms both: the code to write and the sentence to say.

🧠 One-screen mental model

        HOW INFOSYS GRADES A CODING ANSWER

   CLARIFY   β†’  restate the problem, ask about size, nulls, sorted?
   BRUTE     β†’  state the naive O(n^2) so you always have a working answer
   OPTIMISE  β†’  name the trick + the new time/space, THEN code it
   NARRATE   β†’  talk every line; dry-run one normal + one edge case

   A correct SILENT answer scores lower than a narrated near-miss.
   They are hiring someone who reasons out loud on a team.

Matrix & math trap β€” the Bulb Switcher

Scenario: n bulbs start OFF. On pass i you toggle every bulb at a multiple of i (pass 1 toggles all, pass 2 toggles 2,4,6…). After n passes, how many bulbs are ON?

Answer:

  • Brute force = two nested loops toggling a boolean array β†’ O(nΒ²) time. Correct, but it's the answer that fails you β€” they want the insight.
  • The insight: bulb k is toggled once per divisor of k. Divisors come in pairs (d and k/d), so the count is even β†’ bulb ends OFF…
  • …except perfect squares, where one divisor is unpaired (d == k/d) β†’ odd count β†’ bulb ends ON.
  • The O(1) realisation: the answer is simply the number of perfect squares ≀ n = floor(sqrt(n)).
import math
def bulbs(n):
    return int(math.isqrt(n))     # n=100 -> 10  (1,4,9,...,100)
int bulbs(int n){ return (int) Math.floor(Math.sqrt((double) n)); }

🧠 Memory map: Toggles = divisor count; divisors pair up except in perfect squares. Hook: "Only perfect squares survive β†’ √n."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why exactly do only perfect squares end ON? β€” Every divisor d pairs with k/d, giving an even number of toggles; only when d == k/d (a perfect square) is one divisor unpaired, making the toggle count odd.
  • ↳↳ Deepest: Generalise it β€” after n passes, which bulbs are toggled an odd number of times, and how would you list them in O(√n)? β€” The perfect squares 1,4,9,…; iterate i from 1 while i*i <= n and emit i*i β€” O(√n) to enumerate, O(1) to just count.

Dynamic programming on strings β€” Edit Distance

Scenario: Minimum single-character inserts, deletes, or substitutions to turn string a (len m) into b (len n). (Levenshtein distance.)

Answer:

  • State: dp[i][j] = edit distance between the first i chars of a and first j of b.
  • Transition: if a[i-1] == b[j-1] β†’ carry dp[i-1][j-1] (no cost); else 1 + min of delete dp[i-1][j], insert dp[i][j-1], substitute dp[i-1][j-1].
  • Base cases: dp[i][0] = i (delete all), dp[0][j] = j (insert all).
  • Complexity: O(mΒ·n) time; space O(mΒ·n) naive, reducible to O(min(m,n)) with two rolling rows.
        ""   r   o   s
   ""    0   1   2   3
   h     1   1   2   3
   o     2   2   1   2
   r     3   2   2   2
   s     4   3   3   2
   e     5   4   4   3   <-- horse -> ros = 3
def edit_distance(a, b):
    m, n = len(a), len(b)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0] = i
    for j in range(n+1): dp[0][j] = j
    for i in range(1, m+1):
        for j in range(1, n+1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
    return dp[m][n]

🧠 Memory map: Match β†’ carry the diagonal; differ β†’ 1 + min(up, left, diagonal). Hook: "Same? steal the diagonal. Differ? 1 + cheapest neighbour."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: How do you cut the space from O(mΒ·n) to O(n)? β€” Each cell needs only the previous row and the current row so far; keep two 1-D arrays (prev, cur) and swap them each outer iteration.
  • ↳↳ Deepest: If insertion and deletion cost 1 but substitution costs 2, what changes β€” and what problem does that reduce to? β€” Substitution is never worth it (delete+insert = 2 = same), so it becomes the Longest Common Subsequence distance: edits = m + n βˆ’ 2Β·LCS(a,b).

Longest Palindromic Substring

Scenario: Return the longest contiguous substring of s that reads the same both ways.

Answer:

  • Expand around centre (preferred): a palindrome mirrors around a centre; there are 2nβˆ’1 centres (each char, and each gap). Expand outward while characters match.
  • Complexity: O(nΒ²) time, O(1) space β€” the version they want written live.
  • DP alternative: dp[i][j] = true if s[i..j] is a palindrome; dp[i][j] = (s[i]==s[j]) && (jβˆ’i<2 || dp[i+1][jβˆ’1]). O(nΒ²) time and O(nΒ²) space β€” mention it, don't lead with it.
  • Name Manacher's (O(n)) only as "if n is very large" β€” signalling range without over-engineering.
def longest_palindrome(s):
    if not s: return ""
    start = end = 0
    def expand(l, r):
        while l >= 0 and r < len(s) and s[l] == s[r]:
            l -= 1; r += 1
        return l + 1, r - 1
    for i in range(len(s)):
        for l, r in (expand(i, i), expand(i, i + 1)):   # odd + even centres
            if r - l > end - start:
                start, end = l, r
    return s[start:end + 1]

🧠 Memory map: Every palindrome has a centre; try all 2nβˆ’1 centres and grow outward. Hook: "Grow from the middle, both parities."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why must you expand from two kinds of centre? β€” Odd-length palindromes centre on a character (i,i); even-length ones centre on the gap between two characters (i,i+1). Missing the gap centres misses all even-length answers.
  • ↳↳ Deepest: Longest palindromic substring vs subsequence β€” different answers and different algorithms? β€” Substring must be contiguous (expand-around-centre / Manacher). Subsequence can skip characters and is a DP: LPS(s) = LCS(s, reverse(s)), O(nΒ²).

Arrays & strings β€” two-pointer, sliding window, immutability

Scenario: The manipulation techniques Infosys screens lean on, and the string gotcha they love.

Answer:

  • Two-pointer: two indices converging (or one chasing) to avoid a nested loop. Canonical: pair-sum in a sorted array in O(n) β€” move i in when the sum is too small, j in when too big.
  • Sliding window: a moving contiguous range with running state β€” turns many O(nΒ²) substring/subarray problems into O(n). Canonical: longest substring without repeating characters.
  • String immutability: Java / Python / C# strings are immutable β€” every "edit" allocates a new object, so concatenating in a loop is O(nΒ²). Use StringBuilder (Java) or "".join(list) (Python).
  • In-place means mutate the input at O(1) extra space β€” reverse via the two-pointer swap; rotate-by-k = reverse-whole then reverse-two-parts.
def two_sum_sorted(a, target):        # O(n), O(1)
    i, j = 0, len(a) - 1
    while i < j:
        s = a[i] + a[j]
        if s == target: return (i, j)
        i, j = (i + 1, j) if s < target else (i, j - 1)
    return None

def longest_unique(s):                # sliding window, O(n)
    seen, start, best = {}, 0, 0
    for i, c in enumerate(s):
        if c in seen and seen[c] >= start:
            start = seen[c] + 1
        seen[c] = i
        best = max(best, i - start + 1)
    return best

🧠 Memory map: Sorted or converging β†’ two pointers; contiguous window with a running constraint β†’ sliding window; "why is my string loop slow?" β†’ immutability. Hook: "Sorted=pointers, window=substring, strings=immutable."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Interviewer says "swap two characters in a Java String in place." What's the catch? β€” You can't β€” String is immutable. Convert to char[], swap, then new String(arr). Naming the constraint is the answer they're after.
  • ↳↳ Deepest: Two-pointer needs sorted data but sorting is O(n log n) β€” when is the hash-map O(n) two-sum better, and what's the space trade? β€” Unsorted input where you can't afford to sort, or you need original indices: a hash map gives O(n) time at O(n) space; two-pointer is O(1) space but needs the array sorted first.

I02 β€” Object-Oriented Programming & Memory Management

🎯 Why this matters for Infosys: OOP and memory are the L1/L2 fundamentals round β€” they're testing whether you understand why, not whether you can recite four words. The winning move on every question is the crisp distinction (abstraction vs encapsulation, overloading vs overriding, stack vs heap) plus a one-line code example. Say the distinction, show the snippet, name the trap.

🧠 One-screen mental model

        THE FOUR DISTINCTIONS THEY PROBE

   Abstraction  vs  Encapsulation   -> design (hide complexity) vs
                                        implementation (hide data)
   Overloading  vs  Overriding      -> compile-time (declared type) vs
                                        run-time (actual object)
   Abstract cls vs  Interface       -> "is-a" + shared state vs
                                        "can-do" capability, multiple
   Stack        vs  Heap            -> frames/locals/refs vs
                                        every `new` object, GC-managed

The four pillars β€” abstraction vs encapsulation

Scenario: "Explain the OOP pillars β€” and the difference between abstraction and encapsulation." The trap is treating the last two as the same thing.

Answer:

  • Abstraction β€” expose what an object does, hide how. A design concern: interfaces, contracts, hiding complexity.
  • Encapsulation β€” bundle data + methods and restrict direct access via private fields + getters/setters. An implementation concern: hiding data and protecting invariants.
  • Inheritance β€” a subclass reuses/extends a superclass ("is-a"); prefer composition when it's really "has-a".
  • Polymorphism β€” one interface, many implementations (next subsection).
  • The one-liner: "Abstraction hides complexity; encapsulation hides data. One is design, one is implementation."
class Account {
  private double balance;                 // encapsulated state
  public void deposit(double a){ if(a>0) balance += a; }  // guarded invariant
  public double getBalance(){ return balance; }
}

🧠 Memory map: Abstraction = the steering wheel (what); encapsulation = the sealed engine (data protected). Hook: "Abstraction = design/what, Encapsulation = data/how."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Give a case where you have encapsulation but not abstraction. β€” A private field with a public getter/setter and no higher-level contract: state is protected (encapsulated) but callers still see the raw data model β€” no complexity is abstracted away.
  • ↳↳ Deepest: "Prefer composition over inheritance" β€” why, with a concrete failure of inheritance? β€” Inheritance couples you to the parent's implementation and breaks the Liskov substitution rule when the "is-a" is false (e.g. Stack extends Vector exposes insertAt, letting callers violate LIFO). Composition exposes only what you delegate.

Polymorphism β€” overloading vs overriding

Scenario: "Difference between compile-time and run-time polymorphism?" A favourite output-prediction trap hides here.

Answer:

  • Overloading = compile-time (static / early binding). Same method name, different parameter list; the compiler picks the version from the declared argument types/count.
  • Overriding = run-time (dynamic / late binding). A subclass redefines a superclass method with the same signature; the JVM picks the version from the actual object at runtime (via the vtable/method table).
  • The trap: overloading is resolved by the declared (static) type; overriding by the actual (runtime) type. That single sentence answers most "what does this print?" puzzles.
class Animal { String speak(){ return "..."; } }
class Dog extends Animal { @Override String speak(){ return "Woof"; } }

Animal a = new Dog();
a.speak();            // "Woof"  -> overriding, runtime type wins
// add(int,int) vs add(double,double) -> overloading, compiler decides

🧠 Memory map: OverLOADING = compiler + parameter list; overRIDING = runtime + actual object. Hook: "Load at compile, Ride at runtime."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Can you override a static or private method? β€” No. static methods are hidden, not overridden (resolved by declared type); private methods aren't inherited so aren't polymorphic. Only instance methods with sufficient visibility are virtual in Java.
  • ↳↳ Deepest: Animal a = new Dog(); β€” if Animal has an overloaded feed(Animal) and feed(Dog), and you call a.feed(a), which runs and why? β€” feed(Animal) β€” overloads are chosen at compile time by the declared type of a (Animal), even though the object is a Dog. Overload resolution never uses the runtime type.

Abstract class vs Interface (and the diamond)

Scenario: "When would you use an abstract class over an interface?" Plus multiple-inheritance resolution.

Answer:

  • Abstract class β€” can hold instance state, constructors, and concrete methods; a class extends only one. Use for "is-a" with shared code/state (e.g. AbstractList).
  • Interface β€” a capability/contract; only public static final constants, but a class can implement many. Java 8+ adds default and static methods, Java 9+ private helpers. Use for "can-do" shared by unrelated types (Comparable, Runnable).
  • Default rule: program to interfaces; reach for an abstract class only when there's genuine shared state or partial implementation.
  • Diamond problem: Java bans two-class inheritance to avoid ambiguous state; two interfaces with the same default method force you to disambiguate explicitly.
interface A { default String hi(){ return "A"; } }
interface B { default String hi(){ return "B"; } }
class C implements A, B {
  @Override public String hi(){ return A.super.hi(); }  // MUST resolve, else compile error
}

🧠 Memory map: Abstract = is-a + state + one parent; Interface = can-do + no state + many. Hook: "Class shares code, Interface shares capability."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Since Java 8 interfaces have method bodies, why keep abstract classes at all? β€” State. Interfaces still can't hold instance fields or constructors β€” if implementations must share mutable state or construction logic, only an abstract class can carry it.
  • ↳↳ Deepest: How does C++ resolve the diamond differently from Java? β€” C++ uses virtual inheritance (class D : virtual public Base) so only one shared Base subobject exists; Java sidesteps it by forbidding multiple class inheritance and forcing explicit X.super.method() resolution for conflicting interface defaults.

Memory β€” stack, heap, GC, and vtables

Scenario: "Where do variables live, and how is memory reclaimed?" Then the C++/Java dispatch contrast.

Answer:

  • Stack β€” method frames, local primitives, and references. LIFO, per-thread, tiny and fast, auto-freed when the frame pops. Overflow = deep/infinite recursion β†’ StackOverflowError.
  • Heap β€” every object created with new and all arrays. Shared across threads, GC-managed, larger and slower. Exhaustion β†’ OutOfMemoryError.
  • In Person p = new Person(); the reference p is on the stack, the object is on the heap.
  • Garbage collection: an object is collectable when no chain of references from a GC root reaches it (handles cycles, unlike ref-counting). Generational: most objects die young β†’ cheap Young-gen minor GCs, rare Old-gen major GCs; mark-sweep-compact.
  • Leaks still happen via lingering references: ever-growing static collections, un-removed listeners, unclosed resources.
  • Dispatch: C++ builds a vtable per class and each polymorphic object holds a vptr to it β€” you opt in with virtual. Java is virtual by default; the JIT can devirtualise/inline hot monomorphic calls.
Person p = new Person();   // p -> stack, Person object -> heap
p = null;                  // object now unreachable -> eligible for GC

🧠 Memory map: Reference on the stack, object on the heap; GC frees the unreachable, not the unreferenced-by-you. Hook: "Stack = frames & refs, Heap = new objects, GC = reachability."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Can you have a memory leak in a garbage-collected language like Java? β€” Yes β€” via unintended reachability: static maps that only grow, listeners never de-registered, caches without eviction. The GC can't collect what's still referenced.
  • ↳↳ Deepest: Why must a C++ base-class destructor be virtual, and what's the Java parallel? β€” Deleting a derived object through a base pointer with a non-virtual destructor is undefined behaviour β€” the derived destructor never runs, leaking resources. Java has no destructors; you release non-memory resources deterministically with try-with-resources/AutoCloseable, not finalize().

I03 β€” Database Management Systems & Advanced SQL

🎯 Why this matters for Infosys: DBMS is guaranteed on the panel and the online test. They probe four things: command categories (and the DELETE/TRUNCATE/DROP trap), indexing that kills full-table scans, joins + window functions to solve "Nth highest salary" cleanly, and ACID/isolation. Answer with the exact behaviour and, where it helps, the exact SQL.

🧠 One-screen mental model

        THE FOUR DBMS PILLARS

   COMMANDS   DDL / DML / DCL / TCL  -> DELETE(row,logged,rollback)
                                        vs TRUNCATE(all,DDL) vs DROP(gone)
   INDEXES    B-Tree, clustered/non   -> kill the FULL TABLE SCAN
   JOINS+WIN  inner/outer/cross +      -> ROW_NUMBER/RANK/DENSE_RANK
              window functions            solve Nth-highest
   ACID       Atomic/Consistent/       -> isolation levels vs
              Isolated/Durable            dirty/non-repeatable/phantom

Command categories & DELETE vs TRUNCATE vs DROP

Scenario: "Categorise SQL commands, then tell me the difference between DELETE, TRUNCATE and DROP β€” and which is safe in production."

Answer:

  • DDL (Data Definition) β€” CREATE, ALTER, DROP, TRUNCATE. DML (Manipulation) β€” INSERT, UPDATE, DELETE, MERGE. DCL β€” GRANT, REVOKE. TCL β€” COMMIT, ROLLBACK, SAVEPOINT.
  • DELETE = DML: removes rows, supports WHERE, logs each row, fires triggers, rollback-able, does not reset identity. Slower on big tables.
  • TRUNCATE = DDL: removes all rows by deallocating pages, no WHERE, minimal logging, resets identity/auto-increment, no triggers, fast. Rollback is engine-dependent.
  • DROP = DDL: removes the rows and the table structure itself.
  • Production-safe answer: "DELETE … WHERE β€” it's logged, scoped and rollback-able. I never TRUNCATE/DROP in production without a backup and change window, because in several engines they auto-commit and can't be undone."

🧠 Memory map: DELETE = surgical & recoverable; TRUNCATE = empty-the-whole-table fast; DROP = table gone. Hook: "DELETE rows, TRUNCATE table, DROP structure."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Can you roll back a TRUNCATE? β€” It depends on the engine: SQL Server logs page deallocations and can roll it back inside a transaction; MySQL and Oracle treat it as DDL that implicitly commits, so it can't be rolled back. Answer with the nuance, never a flat yes/no.
  • ↳↳ Deepest: After TRUNCATE vs DELETE on a table with an identity column, what differs on the next insert? β€” TRUNCATE resets the identity seed (next id = start), DELETE keeps the counter advancing from the last value. TRUNCATE also releases the storage; DELETE may leave high-water-mark bloat until a rebuild.

Indexing & query optimisation

Scenario: "How would you speed up a slow query / how do indexes work?"

Answer:

  • B-Tree index β€” the default; a balanced sorted tree giving O(log n) lookups, range scans and ordered reads instead of an O(n) full-table scan. Serves =, <, >, BETWEEN, ORDER BY, and prefix LIKE 'abc%'.
  • Clustered index β€” defines the physical row order (one per table; the leaf level is the data). Non-clustered β€” a separate structure with a pointer back to the row (many per table).
  • Composite index on (a,b,c) obeys the left-prefix rule: usable for a, a,b, a,b,c β€” not a filter on b alone.
  • Covering index β€” includes every column a query needs β†’ index-only scan, no table touch.
  • Reading a plan: the enemy is a Full Table / Seq Scan on a big table. Common causes: a function/cast on the indexed column (WHERE YEAR(dt)=2024 β€” rewrite as a range), a leading wildcard LIKE '%x', low selectivity, or stale statistics.

🧠 Memory map: Index to kill the full-table scan; watch the left-prefix rule and functions-on-columns that disable it. Hook: "No function on the indexed column; obey the left prefix."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why can WHERE YEAR(order_date) = 2024 ignore an index on order_date, and how do you fix it? β€” Wrapping the column in a function makes it non-sargable, so the B-Tree can't seek. Rewrite as a range: order_date >= '2024-01-01' AND order_date < '2025-01-01'.
  • ↳↳ Deepest: "Add an index to everything" β€” why is that wrong? β€” Every index speeds reads but slows writes (each INSERT/UPDATE/DELETE maintains it) and costs storage; unused indexes are pure overhead. Index the predicates you actually run and drop the rest.

Joins & window functions β€” the Nth-highest weapon

Scenario: "Explain the joins, then write a query for the 2nd (or Nth) highest salary."

Answer:

  • INNER = rows matching in both. LEFT = all left rows + matches (NULLs where none). RIGHT = mirror. FULL OUTER = all rows from both. CROSS = Cartesian product. SELF = table joined to itself (employeeβ†’manager).
  • Window functions number rows within a PARTITION by an ORDER BY; the difference is tie handling:
  • ROW_NUMBER() β€” always unique (1,2,3,4).
  • RANK() β€” skips after ties (1,1,3).
  • DENSE_RANK() β€” no gaps (1,1,2).
  • Nth highest distinct salary β†’ use DENSE_RANK so ties count once.
-- Nth highest DISTINCT salary
SELECT DISTINCT salary FROM (
  SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employees
) t WHERE rnk = :N;

-- Highest paid per department
SELECT * FROM (
  SELECT e.*, ROW_NUMBER() OVER
     (PARTITION BY dept_id ORDER BY salary DESC) AS rn
  FROM employees e
) x WHERE rn = 1;

🧠 Memory map: RANK skips, DENSE_RANK doesn't, ROW_NUMBER is unique; Nth-distinct β†’ DENSE_RANK. Hook: "Distinct value = DENSE_RANK; specific row = ROW_NUMBER."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: For "2nd highest salary", when do RANK and DENSE_RANK give different answers? β€” When the top salary is tied. DENSE_RANK=2 returns the next distinct value; RANK=2 returns nothing (it jumped 1,1,3). If they want the 2nd distinct amount, DENSE_RANK is correct.
  • ↳↳ Deepest: Write "Nth highest" without window functions. β€” Correlated subquery: SELECT DISTINCT salary FROM employees e1 WHERE N-1 = (SELECT COUNT(DISTINCT salary) FROM employees e2 WHERE e2.salary > e1.salary); β€” or ORDER BY salary DESC LIMIT 1 OFFSET N-1 for row semantics (dialect-dependent).

ACID & concurrency control

Scenario: "Explain ACID, the read anomalies, and isolation levels."

Answer:

  • Atomicity β€” all statements commit or none do. Consistency β€” every transaction moves the DB between valid states (constraints hold). Isolation β€” concurrent transactions don't corrupt each other's view. Durability β€” once committed, it survives a crash (write-ahead log).
  • Anomalies: Dirty read = reading another txn's uncommitted change. Non-repeatable read = a row changes value between two reads (another txn updated+committed). Phantom read = new rows appear in a repeated range query (another txn inserted).
  • Isolation levels trade correctness for concurrency:
Level Dirty Non-repeatable Phantom
READ UNCOMMITTED βœ” possible βœ” βœ”
READ COMMITTED (common default) ✘ βœ” βœ”
REPEATABLE READ ✘ ✘ βœ”*
SERIALIZABLE ✘ ✘ ✘
  • Trade-off to state: higher isolation = more locking = less throughput. (*MySQL InnoDB's REPEATABLE READ uses next-key locks and largely prevents phantoms too.)

🧠 Memory map: ACID = all-or-nothing, valid, isolated, durable; anomalies get worse as isolation drops. Hook: "Dirty=uncommitted, Non-repeatable=value changed, Phantom=rows appeared."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Which anomaly does REPEATABLE READ still permit per the SQL standard? β€” Phantom reads β€” a repeated range query can see newly-inserted matching rows. Only SERIALIZABLE fully prevents phantoms in the standard (engine locking like InnoDB's next-key can prevent them earlier).
  • ↳↳ Deepest: Two transactions each hold a lock the other needs β€” what is it and how does the DBMS handle it? β€” A deadlock. The engine runs deadlock detection (a wait-for graph), picks a victim, and rolls it back with an error to retry. Prevention: acquire locks in a consistent order and keep transactions short.

I04 β€” Templating Paradigm Shift: AMPscript β†’ Handlebars.js

🎯 Why this matters for Infosys: the role pairs your SFMC templating past with a Handlebars-based stack. The single sentence they want to hear: AMPscript is a server-side scripting language with data access; Handlebars is a logic-less presentation template that renders a pre-built JSON context and cannot fetch data. Everything β€” lookups, comparisons, logic β€” moves upstream. Master that and the mapping is direct.

In this module β€” 4 sections

  1. 🧠 One-screen mental model
  2. Architecture & execution differences
  3. Conditionals & custom helpers
  4. Security & escaping

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

🧠 One-screen mental model

        AMPSCRIPT vs HANDLEBARS β€” WHERE THE LOGIC LIVES

   AMPSCRIPT (server-side, has data access)
     Lookup() ── query DE mid-render ── branch ── output   (all in template)

   HANDLEBARS (logic-less presentation)
     App layer: fetch + join + decide ──> flat JSON "view model"
                                              β”‚
                                       {{ template }} just paints it

Architecture & execution differences

Scenario: "What's the fundamental difference between AMPscript and Handlebars?"

Answer:

  • AMPscript runs server-side inside SFMC at send/render and has data access β€” Lookup(), LookupRows(), LookupOrderedRows() query Data Extensions mid-render. It has variables (VAR @x), arithmetic, and full comparison.
  • Handlebars is logic-less β€” it renders a JSON context handed to it and cannot reach a database. No variables, no Lookup, comparisons need a helper.
  • The consequence to articulate: all data retrieval and business logic move into the application layer (e.g. a Node service) that assembles a flat view model; the template only paints it.
  • Why the JD pairs them: both do the same job β€” templating/personalisation β€” but AMPscript does fetch+decide+render in one pass, Handlebars splits fetch/decide (upstream) from render (template).
{{firstName}}          {{! HTML-escaped }}
{{{emailBodyHtml}}}    {{! raw HTML β€” deliberate }}

🧠 Memory map: AMPscript can talk to the database mid-render; Handlebars only paints a context you built first. Hook: "AMPscript fetches; Handlebars only paints."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Your AMPscript email does a Lookup() for loyalty tier at render. How do you deliver the same in a Handlebars stack? β€” Do the lookup upstream in the app: query the tier, fold it into the JSON context ({ tier: "Gold", isGold: true }), and pass that to the template β€” the template never queries anything.
  • ↳↳ Deepest: Why is "logic-less" a design choice, not a limitation? β€” It forces separation of concerns: data/logic sit in testable application code, templates stay pure presentation. That makes templates safe for non-engineers to edit and impossible to slow down with a mid-render DB call.

Conditionals & custom helpers

Scenario: "How do you do a conditional comparison in Handlebars?" (There's no ==.)

Answer:

  • {{#if x}} tests truthiness only β€” no ==, >, or AND.
  • Option 1 (preferred): pre-compute the boolean upstream β€” the context arrives with isGold: true already decided. Keeps the template genuinely logic-less.
  • Option 2: register a custom helper when comparison must live in the template.
  • {{#each}} gives loop metadata: {{@index}}, {{@first}}, {{@last}}, {{this}}, and parent scope via {{../x}}. Reusable markup β†’ partials {{> productCard}}.
Handlebars.registerHelper('eq', function (a, b, options) {
  return a === b ? options.fn(this) : options.inverse(this);
});
{{#eq tier "Gold"}}Gold offer{{else}}Standard{{/eq}}

{{#each products}}
  {{@index}} β€” {{this.name}} {{#if @first}}(first){{/if}}
{{/each}}

🧠 Memory map: #if = truthiness; comparison = pre-compute a boolean or register eq. Hook: "No == in Handlebars β€” pre-compute or write a helper."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: In the eq helper, what do options.fn(this) and options.inverse(this) do? β€” options.fn(this) renders the block body (the {{#eq}}…) with the current context; options.inverse(this) renders the {{else}} branch. Returning one or the other is what makes it a block helper.
  • ↳↳ Deepest: Why is pushing comparisons into helpers sometimes considered an anti-pattern? β€” It smuggles business logic back into the "logic-less" template, scattering decisions across templates and code and making them hard to test. Pre-computing booleans in the view model keeps logic in one testable place β€” helpers are best reserved for presentation (formatting), not decisions.

Security & escaping

Scenario: "When would you use triple braces, and what's the risk?"

Answer:

  • {{value}} β€” HTML-escaped by default: < becomes &lt;. Safe for all user/data-driven text.
  • {{{value}}} β€” raw HTML, injected verbatim. Use only for trusted, pre-sanitised markup (a CMS block).
  • The risk = XSS: any user-supplied string reaching {{{ }}} can inject <script> β†’ stored cross-site scripting.
  • Rule: default to double braces; treat triple braces as a deliberate, audited exception, and sanitise upstream (e.g. DOMPurify) before the value ever enters the context.

🧠 Memory map: Double = escaped/safe (default); triple = raw/dangerous (audited only). Hook: "Two braces safe, three braces XSS."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: A CMS block legitimately contains HTML you must render. How do you use {{{ }}} safely? β€” Sanitise the HTML before it enters the context (server-side allow-list / DOMPurify), store the cleaned string, and only then render with triple braces β€” never sanitise in the template.
  • ↳↳ Deepest: Escaping the value protects the HTML body β€” what context does Handlebars' default escaping not protect? β€” Non-HTML sinks: a value placed inside a URL, an inline onclick/style/JS context, or an attribute without quotes needs context-specific encoding. HTML-entity escaping alone doesn't stop javascript: URLs or attribute-breakout β€” encode per sink.

I05 β€” Enterprise Communications & AWS Pinpoint Architecture

🎯 Why this matters for Infosys: Pinpoint is AWS's multichannel engagement service, and every SFMC concept you own maps to it β€” the vocabulary changes, the model doesn't. The deep shift to nail is subscriber β†’ endpoint, and that orchestration moves out of a boxed tool into AWS (EventBridge + Lambda + the Pinpoint API). Show the mapping table in your head and you sound native.

In this module β€” 4 sections

  1. 🧠 One-screen mental model
  2. Platform architecture mapping
  3. Endpoint & multi-channel audience modeling
  4. Orchestration & event-driven triggers

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

🧠 One-screen mental model

        SFMC ─────────────► AMAZON PINPOINT
   Business Unit            Project (application)
   Subscriber / Contact     Endpoint  (one destination per channel)
   Subscriber Key           User ID   (one user -> many endpoints)
   Data Extension / List    Segment
   Content Builder asset    Message Template
   Journey Builder          Journey
   Automation Studio        EventBridge Β· Lambda Β· schedules
   (email send engine)      Amazon SES underneath  (SPF/DKIM/IP warm-up)

Platform architecture mapping

Scenario: "You know SFMC β€” map it onto Amazon Pinpoint."

Answer:

  • Business Unit β†’ Project (the top-level application container).
  • Subscriber/Contact β†’ Endpoint; Subscriber Key β†’ User ID / Endpoint ID.
  • Data Extension / List β†’ Segment (dynamic or imported).
  • Content Builder asset β†’ Message Template (email, SMS, push, voice, in-app).
  • Journey Builder β†’ Journey; Automation Studio β†’ EventBridge / Lambda / schedules.
  • Send/Campaign β†’ Campaign (segment + template + schedule); AMPscript personalisation β†’ Handlebars-style variables.
  • Tracking β†’ event stream to Kinesis / CloudWatch (raw events; you build the reporting). Email itself is sent by Amazon SES underneath.
  • The sentence: "Pinpoint separates the who (endpoints/segments) from the what (templates) from the when (campaigns/journeys) β€” the same split SFMC makes with DEs, Content Builder and Journey Builder."

🧠 Memory map: Same three-way split (who/what/when), different nouns; SES does the actual email send. Hook: "Who=endpoints, What=templates, When=campaigns."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: SFMC tracking gives you Data Views to query β€” where does that reporting data live in Pinpoint? β€” Pinpoint streams raw events (sends, opens, clicks, bounces) to Kinesis Firehose β†’ S3/Redshift (or CloudWatch); there are no built-in Data Views, so you build the analytics layer on the event stream.
  • ↳↳ Deepest: If SES is the send engine, which SFMC deliverability concepts carry over unchanged? β€” All of them: dedicated IPs, IP warm-up, SPF/DKIM/DMARC alignment, reputation, bounce/complaint handling and suppression β€” SES exposes the same levers, so your deliverability knowledge transfers directly.

Endpoint & multi-channel audience modeling

Scenario: "How does Pinpoint's audience model differ from SFMC's?"

Answer:

  • SFMC centres on the subscriber keyed by Subscriber Key β€” one row holds email, mobile, push as columns.
  • Pinpoint centres on the endpoint β€” a single addressable destination (one email, one phone, one device token) with its own channel type + attributes.
  • A User ID ties multiple endpoints into one person, so one user = many endpoints across channels.
  • Benefit: you can suppress or update one channel without touching the others β€” cleaner true-multichannel modelling.
  • Personalisation uses Handlebars-style variables with attribute fallback: {{User.UserAttributes.FirstName "there"}} β€” the quoted default mirrors AMPscript's IIF(Empty(...)).
User u-8842
 β”œβ”€ Endpoint: EMAIL  akash@x.com
 β”œβ”€ Endpoint: SMS    +9198...
 └─ Endpoint: GCM    <fcm-token>   (Android push)

🧠 Memory map: SFMC = one subscriber row with channel columns; Pinpoint = one endpoint per channel, unified by a User ID. Hook: "One user, many endpoints."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: The same person has email + SMS + push. How is that modelled, and how do you personalise across them? β€” Three endpoints under one User ID; shared data goes in UserAttributes (name, tier), channel-specific data in the endpoint's Attributes. Templates read {{User.UserAttributes.X}} with a fallback default.
  • ↳↳ Deepest: A user opts out of SMS but not email β€” how does the endpoint model make that clean, and what's the SFMC contrast? β€” You disable/opt-out just the SMS endpoint; the email endpoint is untouched. In SFMC's subscriber-centric model, channel opt-outs and status live on the one subscriber record, so per-channel suppression takes extra design (separate lists/attributes).

Orchestration & event-driven triggers

Scenario: "How do event-triggered campaigns work in Pinpoint vs SFMC's Automation Studio?"

Answer:

  • SFMC's Automation Studio is a boxed tool inside the platform; Pinpoint pushes orchestration out into AWS and you compose it.
  • Schedule β†’ EventBridge scheduled rule. Event trigger β†’ app emits an event β†’ EventBridge rule matches β†’ Lambda runs logic β†’ calls the Pinpoint SendMessages API.
  • Custom logic / SSJS-style work β†’ Lambda (Node/Python) with the Pinpoint SDK.
  • Benefit: any AWS service can be injected into the flow; it's serverless, scaling with event volume.
export const handler = async (event) => {
  const pinpoint = new PinpointClient({ region: "us-east-1" });
  await pinpoint.send(new SendMessagesCommand({
    ApplicationId: PROJECT_ID,
    MessageRequest: {
      Addresses: { [event.email]: { ChannelType: "EMAIL" } },
      MessageConfiguration: { EmailMessage: { /* template + substitutions */ } }
    }
  }));
};

🧠 Memory map: EventBridge = the trigger/schedule, Lambda = the logic, Pinpoint API = the send. Hook: "EventBridge fires, Lambda decides, Pinpoint sends."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: An order-placed event must fire a confirmation email. Trace the AWS path. β€” App/API emits order.placed β†’ EventBridge rule matches β†’ triggers a Lambda β†’ Lambda calls Pinpoint SendMessages with the endpoint + transactional template. Optionally the event also streams to Kinesis for analytics.
  • ↳↳ Deepest: What does composing orchestration in AWS buy you β€” and what does it cost β€” versus SFMC's single tool? β€” Buys flexibility and serverless scale (inject any AWS service, pay per event); costs more moving parts and ops (IAM, retries/DLQs, monitoring across services) that a boxed tool hides. The trade is control vs convenience.

I06 β€” Omnichannel Messaging Infrastructure (SMS, RCS, IVR)

🎯 Why this matters for Infosys: beyond email, the comms stack is SMS, RCS and voice. The three signals that read as senior: the GSM-7 vs UCS-2 cost math (one emoji triples the bill), the mandatory SMS fallback for RCS, and SSML control for voice. These are small facts that only hands-on developers know β€” say them unprompted.

In this module β€” 4 sections

  1. 🧠 One-screen mental model
  2. SMS encodings & the financial math
  3. RCS β€” Rich Communication Services
  4. IVR & voice personalization (SSML)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

🧠 One-screen mental model

        THREE CHANNELS, THREE GOTCHAS

   SMS   GSM-7 160/153  vs  UCS-2 70/67   -> one emoji flips encoding
                                             -> cost multiplies (per segment)
   RCS   rich cards/carousels/replies     -> ALWAYS design the SMS fallback
   IVR   TTS + SSML + DTMF                 -> read digits/dates deliberately,
                                             short prompts, always offer agent

SMS encodings & the financial math

Scenario: "Why does adding one emoji blow up an SMS campaign?"

Answer:

  • GSM-7 (plain Latin) = 160 chars single, 153 per segment concatenated.
  • UCS-2 (any Unicode/emoji) = 70 chars single, 67 per segment concatenated.
  • The 7-char drop on concatenation is the UDH header (6 bytes) that lets the phone reassemble multi-part messages.
  • The trap: a single emoji β€” or a curly quote / em-dash pasted from Word β€” flips the whole message to UCS-2, cutting 160β†’70 and multiplying segment count. Billing is per segment, so a 150-char message (1 segment) can become 3 (70+67+13) β€” triple the cost.
  • Constraints: plain text only (no HTML/images), opt-out (STOP) required, 10DLC (US) / DLT (India) registration, shortened links still consume characters, front-load the brand.

🧠 Memory map: Emoji β†’ UCS-2 β†’ 160 becomes 70 β†’ more segments β†’ more money. Hook: "One emoji = UCS-2 = triple the bill."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: A 150-character promo is one segment. Marketing adds a πŸŽ‰. What's the new segment count and why? β€” It becomes UCS-2 at 67 chars/segment, so 150 chars β†’ 3 segments (67+67+16). The emoji re-encoded the entire message, not just itself.
  • ↳↳ Deepest: Besides emoji, what silently forces UCS-2, and how do you defend against it pre-send? β€” Non-GSM characters: curly β€œsmart” quotes, em/en-dashes, ellipsis, accented letters pasted from Word. Defence: run copy through a GSM-7 validator/transliterator that flags or down-converts them before approval.

RCS β€” Rich Communication Services

Scenario: "What is RCS and what changes for you as a developer?"

Answer:

  • RCS is the successor to SMS, delivered via Google's RCS Business Messaging; a verified brand profile gets a name, logo, colour and badge in the thread.
  • Features: rich cards (title, media, up to 4 suggestions), carousels (scrollable cards), suggested replies (tappable chips), suggested actions (dial, open URL, share location, calendar), plus read receipts & typing indicators β€” true two-way messaging.
  • The mandatory rule β€” always author the fallback: if the device/carrier doesn't support RCS, the message degrades to plain SMS. A carousel or a reply chip won't exist on the fallback, so every RCS template needs an SMS version that stands alone.

🧠 Memory map: RCS = branded, rich, interactive β€” but never ship it without its SMS fallback. Hook: "Rich when you can, SMS fallback always."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Your RCS message is a carousel with "Buy" and "Track" action buttons. What must the SMS fallback contain? β€” A self-contained plain-text version with the same intent and a tappable link in place of the buttons (buttons/carousels can't render), so the message still works with zero rich features.
  • ↳↳ Deepest: How would you decide, per recipient, whether to send RCS or SMS β€” and keep cost/UX sane? β€” Check RCS capability for the number first; send RCS where supported, SMS otherwise. Treat RCS as progressive enhancement: author once with an SMS baseline, upgrade to rich where capability + business value justify it, and measure fallback rate.

IVR & voice personalization (SSML)

Scenario: "Design a voice/IVR prompt β€” how do you control how it's read?"

Answer:

  • Voice has no visual channel: keep each prompt short, confirm what you captured, always offer a repeat and an agent.
  • SSML controls the text-to-speech: <speak> (root), <say-as interpret-as="digits|date|currency">, <break time="300ms"/> (pause), <prosody rate/pitch>, <emphasis>, <sub> (say "Doctor" for "Dr.").
  • DTMF = keypad tones for menu input ("press 1"); keep menus short, confirm the selection, allow "press 0 for an agent".
  • Read deliberately: 4821 as "four-eight-two-one" via say-as, not "four thousand eight hundred twenty-one".
<speak>
  Your order <say-as interpret-as="digits">4821</say-as>
  ships <break time="300ms"/> tomorrow.
  Press <emphasis level="strong">1</emphasis> to confirm, or 2 for an agent.
</speak>

🧠 Memory map: No screen β†’ short prompts, confirm, repeat; SSML for how digits/dates/pauses are spoken; DTMF for input. Hook: "Short, confirm, repeat β€” SSML shapes the voice."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why wrap 4821 in <say-as interpret-as="digits"> instead of leaving it as text? β€” Without it the engine reads the cardinal number ("four thousand eight hundred twenty-one"), which is wrong for an order/OTP/account number. say-as forces digit-by-digit reading β€” clearer and unambiguous.
  • ↳↳ Deepest: For an OTP read aloud, what SSML choices make it usable, and what's the accessibility risk you must handle? β€” Read as digits with a small <break> between them and a moderate <prosody rate="slow">; repeat it once. Risk: users need time to write it down and may be hearing-impaired β€” so always pair voice OTP with a repeat option and an alternate channel, never voice-only.

I07 β€” Cross-Client QA & Email Rendering Hacks

🎯 Why this matters for Infosys: this is your home turf β€” the part of the JD you already do daily; only the tool names change. The three things to demonstrate: you know why Outlook breaks (the Word engine), you can map Litmus ↔ Email on Acid without flinching, and you have a dark-mode/responsive strategy that survives clients you can't fully control.

In this module β€” 4 sections

  1. 🧠 One-screen mental model
  2. Outlook rendering-engine quirks
  3. QA tools β€” Litmus vs Email on Acid
  4. Responsive & dark-mode strategy

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

🧠 One-screen mental model

        WHY EMAIL QA IS HARD (AND WHERE)

   OUTLOOK (Windows) = MS WORD engine
     no flex, no grid, no max-width  -> tables + ghost tables + VML
   QA TOOLS  Litmus  <-> Email on Acid   (same workflow, new names)
   RESPONSIVE  fluid-hybrid (ghost tables + max-width inline-block divs)
   DARK MODE  color-scheme meta + prefers-color-scheme
              ...but some clients FORCE-INVERT anyway -> design for it

Outlook rendering-engine quirks

Scenario: "An email renders correctly everywhere except Outlook. Walk me through it."

Answer:

  • Root cause: Windows Outlook renders with the Microsoft Word engine, not a browser β€” so no flexbox, no grid, no max-width, no reliable float, no background-image shorthand.
  • Fixes: nested <table> structure with fixed pixel widths; ghost tables inside <!--[if mso]> conditionals for multi-column layouts that must stack on mobile; VML (<v:roundrect>) for bulletproof buttons and background images; mso-line-height-rule:exactly and mso-padding-alt for spacing.
  • Then re-run the client matrix (Outlook, Gmail, Apple Mail, mobile).
<!--[if mso]><table role="presentation" width="600"><tr><td width="300"><![endif]-->
  <div style="display:inline-block;width:100%;max-width:300px;vertical-align:top;">column</div>
<!--[if mso]></td></tr></table><![endif]-->

🧠 Memory map: Outlook = Word engine β†’ tables, ghost tables, VML, mso- fixes. Hook: "Outlook is Word β€” tables, ghosts, VML."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: What is a "ghost table" and why is it wrapped in <!--[if mso]>? β€” An Outlook-only <table> that enforces fixed column widths. The conditional comment means only Outlook parses it, while modern clients ignore it and use the fluid inline-block divs β€” giving you rigid Outlook layout and responsive everywhere else.
  • ↳↳ Deepest: Why build a button in VML instead of a styled <a> for Outlook? β€” Outlook ignores padding, border-radius and background on an <a>, so the clickable area collapses to the text. VML <v:roundrect> draws a real rounded, filled, fixed-size shape with a reliable click target β€” a "bulletproof button" β€” behind an <!--[if !mso]> normal button for other clients.

QA tools β€” Litmus vs Email on Acid

Scenario: "We use Email on Acid, you've used Litmus β€” is that a problem?"

Answer:

  • No β€” same workflow, different names. Map them directly:
What you do Litmus Email on Acid
Client previews Litmus Previews Email Previews
Pre-send QA sweep Litmus Checklist Campaign Precheck
Code + live preview Litmus Builder Editor
Accessibility In Checklist Strong accessibility checks
Spam / deliverability Spam testing Deliverability & inbox display
  • Your line: "I've used Litmus for cross-client rendering across Outlook, Gmail, Apple Mail and mobile. Email on Acid's Campaign Precheck is the same pre-send sweep under a different name β€” content, links, images, accessibility, spam. The workflow transfers directly."

🧠 Memory map: Litmus Checklist β‰ˆ Campaign Precheck; the sweep is identical. Hook: "Same QA sweep, different label."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Walk your pre-send QA sequence, tool-agnostic. β€” Verify audience + suppression β†’ render across the client matrix β†’ images-off + dark-mode β†’ alt text β†’ link/UTM validation β†’ spam/content check β†’ seed test β†’ deploy. Any production defect earns a root-cause pass + a new checklist line so it can't recur.
  • ↳↳ Deepest: Which checks catch problems rendering previews cannot? β€” Previews are static screenshots; they miss interaction and delivery issues β€” broken/expired links, wrong UTMs, spam-filter triggers, load time, and accessibility (contrast, alt text, semantic order). Those need Checklist/Precheck-style validators plus a real seed send.

Responsive & dark-mode strategy

Scenario: "How do you handle mobile and dark mode across clients?"

Answer:

  • Fluid-hybrid ("spongy") β€” combine ghost tables (Outlook fixed widths) with max-width + display:inline-block divs so modern clients flow fluidly. Robust because it survives clients that strip media queries (Gmail app, some Outlook).
  • Media queries for finer control, but never make critical layout depend on them.
  • Dark mode: declare color-scheme + supported-color-schemes meta, use @media (prefers-color-scheme: dark), and swap logos (light logo on dark).
  • The honest caveat: some clients (Outlook.com, Gmail Android) force-invert regardless β€” so design for graceful behaviour under inversion, not pixel control.
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<style>@media (prefers-color-scheme: dark){ .body{background:#15161B!important} .text{color:#E9E7E2!important} }</style>

🧠 Memory map: Fluid-hybrid for layout (media queries can be stripped); dark mode = meta + query, but expect forced inversion. Hook: "Hybrid survives stripped queries; design for forced dark."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why can't you rely on @media queries for the core layout in email? β€” Several major clients (Gmail app on some setups, older Outlook) strip <style> blocks or ignore media queries, so a layout that only works with them collapses. The fluid-hybrid structure degrades to a sensible single/limited-width layout even with no CSS applied.
  • ↳↳ Deepest: A pure-black logo on transparent PNG vanishes in a force-inverting dark client β€” how do you defend? β€” Give the logo a non-transparent background or a light halo/outline, provide a dark-mode logo swap, and use client-specific overrides ([data-ogsc]/[data-ogsb] for Outlook) β€” because you can influence but not fully control forced inversion.

I08 β€” Comprehensive Infosys Interview Execution Strategy

🎯 Why this matters for Infosys: technical depth gets you shortlisted; execution gets you hired. The panel is grading how you open, how you think aloud while coding, and how you handle a question you can't answer. This chapter is the playbook: the scripts, the 4-step live-coding protocol, and the composure rules β€” rehearse these out loud, because recall under pressure is the real test.

In this module β€” 4 sections

  1. 🧠 One-screen mental model
  2. First 5 minutes & proactive steering
  3. Live coding β€” the think-aloud protocol
  4. Project defensibility & handling unknowns

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

🧠 One-screen mental model

        THE THREE MOMENTS THEY JUDGE

   OPEN     45s intro -> acknowledge gap -> MAP to your stack -> steer
   CODE     clarify -> brute force -> optimise -> narrate every line
   UNKNOWN  don't bluff -> reason from fundamentals -> bound the gap
            -> "here's how I'd find out" is a COMPLETE answer

First 5 minutes & proactive steering

Scenario: Introduce yourself and handle "we use Handlebars/Pinpoint, not AMPscript/SFMC."

Answer:

  • 45-second intro: who you are (comms-stack developer, 4+ yrs, enterprise email/SMS/multichannel), your core depth (templating, personalisation, cross-client QA, the data side), and why you're here.
  • Bridge a domain shift with 3 beats: Acknowledge honestly ("my production depth is AMPscript, not Handlebars") β†’ Map immediately ("same job β€” one has data access, one is logic-less, so logic moves upstream; I've worked the mapping") β†’ Steer to strength ("day-one value is the delivery side β€” Outlook, encoding, deliverability β€” identical across stacks").
  • Never hide a gap β€” name it and bridge it faster than they can probe it.

🧠 Memory map: Open with a tight intro; on any gap, acknowledge β†’ map β†’ steer. Hook: "Acknowledge, Map, Steer."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: They say "but you've never used Handlebars in production." Exact response? β€” "Correct β€” my production templating is AMPscript. They're the same discipline: AMPscript fetches and renders, Handlebars renders a context I build upstream. I've already mapped output, conditionals, loops and helpers β€” so it's a vocabulary switch, not a relearn."
  • ↳↳ Deepest: How do you steer an interview toward your strengths without dodging their question? β€” Answer the question first (never dodge), then bridge: "…and that connects to X, which is where I've done the most depth β€” for example…". You earn the redirect by fully addressing what they asked, then extending into your strong area.

Live coding β€” the think-aloud protocol

Scenario: They share a coding problem. What's your process?

Answer:

  • 1. Clarify constraints & edge cases: empty/null? sorted? signed? input size (does O(nΒ²) pass)? return-on-no-match? Restating buys thinking time and catches misunderstandings.
  • 2. State the brute force β€” "nested loops, O(nΒ²)" β€” so you always have a working answer on the board.
  • 3. Propose the optimal β€” name the trick and the new time/space ("hash map: O(n) time for O(n) space" / "sorted β†’ two pointers, O(1) space").
  • 4. Code cleanly while narrating β€” real names, handle the edge cases you listed, then dry-run one normal and one edge case aloud.
  • The meta-signal: a correct silent solution scores lower than a narrated near-miss. Talk continuously; if you pause, say what you're weighing.

🧠 Memory map: Clarify β†’ brute force β†’ optimise β†’ narrate + dry-run. Hook: "Clarify, Brute, Optimise, Narrate."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: You realise mid-code your approach is wrong. What do you do out loud? β€” Say it: "I see a flaw β€” this misses the duplicate case. Let me switch to a hash-set approach." Catching and correcting your own bug on the record scores higher than a lucky clean run; it shows real debugging.
  • ↳↳ Deepest: Interviewer stays silent as you code. What does that usually mean and how do you use it? β€” Silence is usually "keep going, I'm observing your process" β€” not disapproval. Use it to narrate more: state assumptions, complexity, and the next step. If truly stuck, ask a specific question ("is memory or latency the priority here?") to reopen dialogue.

Project defensibility & handling unknowns

Scenario: They challenge a design decision, then ask something you don't know.

Answer:

  • Defend a decision with context β†’ options β†’ trade-off β†’ decision β†’ verification: "At 2M rows nightly the options were file transfer or per-row API; I chose file/SFTP because API rate limits made 2M calls fragile and slow; I kept the API only for the real-time slice; I verified with a row-count check that halts on anomaly."
  • Don't bluff β€” fabrication is the fastest disqualifier; traps exist to catch exactly that.
  • Reason from fundamentals: "I haven't used that specific API, but from first principles it should behave like X because Y."
  • Bound the gap, then bridge: "I haven't administered Data Cloud, but I know exactly where it meets my work β€” it activates a segment into the engagement layer as a data extension, which I treat like any audience."
  • Composure rule: a hard question often means you cleared the earlier bar β€” slow down, think aloud; "I don't know that, but here's how I'd find out" is a complete, strong answer.

🧠 Memory map: Defend with a stated trade-off; on unknowns, reason from fundamentals and bound honestly β€” never bluff. Hook: "A trade-off defended beats a fact recited; honesty beats bluffing."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: "Why did you do it this way and not the obvious way?" β€” how do you answer without sounding defensive? β€” Lead with the constraint, not the ego: "Given [constraint], the obvious way fails at [X], so I traded [A] for [B]. If [constraint] changed, I'd revisit it." A decision tied to a trade-off reads senior; "that's how we always did it" reads junior.
  • ↳↳ Deepest: They keep drilling until you genuinely hit the edge of your knowledge. What's the ideal final move? β€” Name the boundary precisely, show the adjacent competence, and state how you'd close it: "That's past where I've worked hands-on. Based on fundamentals I'd expect X; to be sure I'd check the docs / prototype / ask the team." That converts a limit into evidence of judgment and honesty β€” which is what the drill was testing.

I09 β€” Tutorial: Handlebars.js (Zero β†’ Hero)

🎯 What you'll master: the full jump from AMPscript to Handlebars β€” the mental model, the context JSON contract, writing custom helpers, and shipping XSS-safe templates. By the Hero part you can defend every escaping and logic decision in an L3 panel.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

The one idea everything hangs on

AMPscript is a server-side scripting language with data access β€” it runs Lookup() against a Data Extension mid-render. Handlebars is a logic-less presentation template β€” it renders a JSON context handed to it and cannot fetch data. Every lookup and decision moves upstream, into the application that builds the context.

Core syntax

  • {{value}} β€” output, HTML-escaped (the safe default).
  • {{{value}}} β€” output raw HTML (dangerous β€” see Part 3).
  • {{#if x}}…{{else}}…{{/if}} β€” block helper, tests truthiness only (no ==).
  • {{#unless x}} β€” negated if.
  • {{#each list}}…{{/each}} β€” iterate; metadata {{@index}}, {{@first}}, {{@last}}, {{this}}.
  • {{#with obj}} β€” change scope. {{../x}} β€” reach the parent scope.
  • {{> partial}} β€” include a reusable partial. {{!-- comment --}}.

AMPscript β†’ Handlebars, at a glance

AMPscript
%%=v(@firstName)=%%
%%[ IF @tier == "Gold" THEN ]%%Gold%%[ ELSE ]%%Std%%[ ENDIF ]%%
%%[ FOR @i=1 TO @n DO ]%% ... %%[ NEXT @i ]%%
Lookup("DE","col","key",@k)
Handlebars
{{firstName}}
{{#if isGold}}Gold{{else}}Std{{/if}}
{{#each rows}} ... {{/each}}
<!-- impossible: pass data in via the context -->

Part 2 β€” The Intermediate Application

The context JSON is a contract

The template renders exactly one object β€” the context (a.k.a. view model). Build it upstream by doing every Lookup() and every decision in application code, then hand the template a flat, pre-decided object.

// Upstream (Node): fetch + join + DECIDE, then render
const ctx = {
  firstName: user.firstName || "there",
  isGold: user.tier === "Gold",          // pre-computed boolean
  order: { id: order.id, amountCents: order.total },
  products: cart.map(p => ({ name: p.name, priceCents: p.price }))
};
const html = Handlebars.compile(source)(ctx);
<p>Hi {{firstName}}.</p>
{{#if isGold}}<p>Your Gold reward is ready.</p>{{/if}}
<ul>{{#each products}}<li>{{name}} β€” {{priceCents}}</li>{{/each}}</ul>

Custom helpers β€” because there is no ==

{{#if}} only tests truthiness. To compare, you either pre-compute a boolean upstream (preferred β€” keeps the template logic-less) or register a helper. Both are correct; naming both is the strong-candidate signal.
// Block helper: renders the block (fn) or the {{else}} branch (inverse)
Handlebars.registerHelper('eq', function (a, b, options) {
  return a === b ? options.fn(this) : options.inverse(this);
});
// Inline helper: returns a formatted value (presentation only)
Handlebars.registerHelper('money', cents => '$' + (cents / 100).toFixed(2));
{{#eq tier "Gold"}}Gold offer{{else}}Standard{{/eq}}
Total: {{money order.amountCents}}

Partials for reuse (the ContentBlockByName equivalent)

Handlebars.registerPartial('productCard', '<li>{{name}} β€” {{money priceCents}}</li>');
// usage: {{#each products}}{{> productCard}}{{/each}}

Part 3 β€” The Hero (Interview Level)

XSS sanitization β€” the question they are fishing for

Escaping is the default for a reason. {{value}} encodes < to &lt;. {{{value}}} injects raw markup β€” so any user-supplied string reaching triple braces is a stored-XSS vector. Rule: default to double braces; treat triple braces as an audited exception, and sanitize upstream before the value enters the context.
import DOMPurify from 'isomorphic-dompurify';
// Sanitize ONCE, upstream β€” never in the template
ctx.bodyHtml = DOMPurify.sanitize(cmsBlock, { ALLOWED_TAGS: ['b','i','a','p','br','ul','li'] });
{{{bodyHtml}}}   {{! safe ONLY because it was sanitized upstream }}

Edge case: escaping protects HTML body, not every sink

Sink Default {{ }} enough? Why
HTML text/body βœ… yes entity-encodes < > & "
href="{{url}}" ⚠️ no a javascript: URL survives entity-encoding β€” validate the scheme
onclick="{{x}}" / inline JS ❌ no JS context needs JS-encoding, not HTML-encoding
Unquoted attribute ❌ no value can break out of the attribute β€” always quote

Production debugging & optimization

  • Pre-compile templates at build/startup with Handlebars.precompile β€” parsing on every render is wasted CPU at scale.
  • {{log}} and rendering a partial in isolation isolate "is it the data or the template?" β€” 90% of "personalization is blank" bugs are a missing/renamed context key, not template syntax.
  • Keep helpers presentational. Comparison/branching helpers scatter business logic across templates and are hard to test β€” push decisions into the view model; reserve helpers for formatting (money, dates, pluralization).
  • Never let a template do I/O. If you feel the urge to fetch inside a helper, the architecture is wrong β€” the fetch belongs upstream.
L3 closing line: "Handlebars being logic-less isn't a limitation β€” it's a security and testability boundary. Data and decisions live in tested application code; the template only paints. That's exactly why {{{ }}} is a deliberate, sanitized exception and never a default."

I10 β€” Tutorial: AWS Pinpoint & SES (Zero β†’ Hero)

🎯 What you'll master: the endpoint audience model, dynamic segments, and event-driven orchestration with EventBridge + Lambda + the Pinpoint API β€” mapped from everything you already know in SFMC, down to the SES deliverability layer.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

The vocabulary map (memorize this table)

SFMC Amazon Pinpoint Note
Business Unit Project (application) top-level container
Subscriber / Contact Endpoint one destination + attributes
Subscriber Key User ID / Endpoint ID one user β†’ many endpoints
Data Extension / List Segment dynamic or imported
Content Builder asset Message Template email/SMS/push/voice/in-app
Journey Builder Journey multi-step orchestration
Automation Studio EventBridge Β· Lambda orchestration lives in AWS
(email send engine) Amazon SES IPs, SPF/DKIM, reputation
The deep shift: SFMC keys on the subscriber (one row, channel columns). Pinpoint keys on the endpoint β€” a single addressable destination per channel β€” and unifies endpoints under a User ID. One user = many endpoints.

An endpoint is just JSON

{
  "ChannelType": "EMAIL",
  "Address": "akash@example.com",
  "Attributes": { "cartValue": ["120"] },
  "User": {
    "UserId": "u-8842",
    "UserAttributes": { "FirstName": ["Akash"], "Tier": ["Gold"] }
  }
}

Part 2 β€” The Intermediate Application

Dynamic segments

A Segment is Pinpoint's Data Extension. Imported = a static CSV/S3 list. Dynamic = a live filter over endpoint attributes that re-evaluates at send time.

{
  "SegmentGroups": { "Groups": [{
    "Dimensions": [{
      "Attributes": { "Tier": { "AttributeType": "INCLUSIVE", "Values": ["Gold"] } },
      "Demographic": { "Channel": { "DimensionType": "INCLUSIVE", "Values": ["EMAIL"] } }
    }],
    "SourceType": "ALL"
  }]}
}

Personalization with attribute fallback

Hi {{User.UserAttributes.FirstName "there"}}, order
{{Attributes.OrderId}} shipped.

The quoted second argument is the default β€” the AMPscript IIF(Empty(...)) equivalent.

Event-driven orchestration

SFMC Automation Studio
Scheduled / API-event automation
inside the platform:
  Entry -> Filter -> Send activity
AWS (composed)
App emits event
  -> EventBridge rule matches
  -> Lambda runs logic
  -> Pinpoint SendMessages API
// order.placed event -> Lambda -> transactional send
export const handler = async (event) => {
  const pinpoint = new PinpointClient({ region: "us-east-1" });
  await pinpoint.send(new SendMessagesCommand({
    ApplicationId: PROJECT_ID,
    MessageRequest: {
      Addresses: { [event.email]: { ChannelType: "EMAIL" } },
      MessageConfiguration: { EmailMessage: { /* template + substitutions */ } }
    }
  }));
};

Part 3 β€” The Hero (Interview Level)

SES is the deliverability layer β€” everything transfers

Under Pinpoint email sits Amazon SES, so every SFMC deliverability lever applies unchanged: dedicated IPs, IP warm-up, SPF/DKIM/DMARC alignment, reputation, bounce/complaint handling, suppression lists. Say this and you sound like you've run production email, not just clicked a console.
  • SPF authorizes sending IPs (TXT record); DKIM signs the message (CNAME records SES gives you); DMARC requires SPF or DKIM to align with the visible From domain. Misalignment β†’ spam folder even with valid DKIM.
  • Configuration Sets attach to sends to route event publishing (opens, clicks, bounces, complaints) to Kinesis Firehose β†’ S3/Redshift β€” this is how you rebuild SFMC's Data Views, because Pinpoint has none.

Edge cases & production debugging

Symptom Root cause Fix
High bounce, new domain cold IP / no warm-up ramp volume over 4–8 weeks, engaged users first
Emails land in spam DKIM/SPF not aligned with From fix DMARC alignment, not just "add DKIM"
Send throttled SES sending quota / rate exceeded request quota increase; queue + drain via SQS
Duplicate sends Lambda ret/at-least-once delivery make the send idempotent (dedupe key per event)
Segment sends to wrong people dynamic segment evaluated stale attrs refresh endpoints before send; check attribute latency

Optimization

  • Idempotency: EventBridge/Lambda is at-least-once β€” a retried event double-sends. Store a processed-event key (DynamoDB) and short-circuit duplicates.
  • Back-pressure: for spikes, put a SQS queue between the producer and the sending Lambda so a burst becomes a controlled drain instead of SES throttling.
  • DLQ everything: attach a dead-letter queue to the Lambda so a poison event is captured, not silently lost.
L3 closing line: "Pinpoint separates the who (endpoints/segments), the what (templates) and the when (campaigns/journeys) β€” the same split SFMC makes β€” but pushes orchestration into composable AWS services. That buys flexibility and serverless scale at the cost of owning idempotency, retries and monitoring myself. SES underneath means my deliverability knowledge transfers 1:1."

I11 β€” Tutorial: Omnichannel Infrastructure β€” SMS, RCS, IVR (Zero β†’ Hero)

🎯 What you'll master: the SMS encoding cost math (the emoji trap), RCS rich payloads with a mandatory SMS fallback, and SSML voice scripting for IVR β€” the small, hands-on facts that separate a developer from a reciter.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

SMS encoding & segment limits

Encoding Single message Per segment (concatenated) Used for
GSM-7 160 chars 153 chars plain Latin, digits, basic punctuation
UCS-2 70 chars 67 chars any emoji / non-GSM char
The 7-character drop on concatenation is the UDH header (6 bytes) that tells the phone how to reassemble multi-part messages. Billing is per segment, so segment count = money.

RCS and IVR in one line each

  • RCS β€” the rich successor to SMS via Google's RCS Business Messaging: verified brand, rich cards, carousels, suggested replies/actions, read receipts.
  • IVR β€” voice menus driven by TTS + SSML (how it's spoken) and DTMF (keypad input).

Part 2 β€” The Intermediate Application

The character-math that controls cost

GSM-7 (cheap)
"Your order 4821 has shipped."
28 chars -> 1 segment (GSM-7)
Cost: 1x
One emoji flips it (UCS-2)
"Your order 4821 has shipped πŸŽ‰"
now UCS-2, 70/segment
same text -> 3 segments -> Cost: 3x

An RCS rich-card payload (with the fallback)

{
  "contentMessage": {
    "richCard": { "standaloneCard": {
      "cardContent": {
        "title": "Order shipped",
        "description": "Arrives tomorrow",
        "suggestions": [
          { "action": { "text": "Track", "openUrlAction": { "url": "https://x.co/t/4821" } } },
          { "reply": { "text": "Contact support" } }
        ]
      }
    }}
  },
  "smsFallback": "Your order 4821 shipped, arrives tomorrow. Track: https://x.co/t/4821"
}

An SSML voice prompt

<speak>
  Your order <say-as interpret-as="digits">4821</say-as>
  ships <break time="300ms"/> tomorrow.
  Your balance is <say-as interpret-as="currency">USD42.50</say-as>.
  Press <emphasis level="strong">1</emphasis> to confirm, or 2 for an agent.
</speak>

Part 3 β€” The Hero (Interview Level)

The traps and how to answer them

Always author the SMS fallback. If the device/carrier can't do RCS, the message degrades to plain SMS β€” a carousel or reply-chip simply won't exist. Every RCS template needs an SMS version that stands alone. "Rich when you can, SMS fallback always."
  • The hidden UCS-2 trigger isn't just emoji β€” curly "smart" quotes, em/en-dashes, ellipsis and accented characters pasted from Word silently force UCS-2. Defense: run copy through a GSM-7 validator/transliterator before approval.
  • OTP read aloud: use <say-as interpret-as="digits"> with a small <break> between digits and <prosody rate="slow">, then repeat once β€” and never rely on voice-only for OTP (accessibility + recall).
  • Regulatory gates: US = 10DLC registration; India = DLT header + template registration. An unregistered template is blocked at the carrier, not by your code.

Debugging & optimization

Symptom Cause Fix
SMS cost 3Γ— expected one non-GSM char β†’ UCS-2 strip/transliterate; validate encoding pre-send
RCS not delivered recipient not RCS-capable capability check first; fall back to SMS
Truncated message segment limit hit mid-word count segments incl. shortened links
IVR says "four thousand…" number not wrapped in say-as force interpret-as="digits"
L3 closing line: "I treat RCS as progressive enhancement over an SMS baseline, I validate encoding before approving copy because one emoji triples the bill, and for voice I script SSML so digits, dates and currency are read unambiguously β€” with a repeat option for accessibility."

I12 β€” Tutorial: Email Development & Outlook Hacks (Zero β†’ Hero)

🎯 What you'll master: why Outlook breaks, and the four fixes that always work β€” ghost tables, VML bulletproof buttons, the fluid-hybrid layout, and dark-mode CSS that survives clients that force-invert.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

The root cause of 90% of email bugs

Windows Outlook (2007–2019 + the classic desktop client) renders with the Microsoft Word engine, not a browser. So: no flexbox, no grid, no max-width, no reliable float, no background-image shorthand, no CSS positioning. Every "renders fine except Outlook" ticket starts here.

The rules of resilient email

  • Tables for layout, not divs β€” nested <table role="presentation"> with fixed pixel widths.
  • Inline your CSS (many clients strip <style> blocks); keep a <style> block only for progressive enhancement (media queries, dark mode).
  • Images need alt text and a styled fallback β€” many clients block images by default.
  • 600px is the safe content width.

Part 2 β€” The Intermediate Application

Ghost tables β€” fluid modern clients, rigid Outlook

A "ghost table" is an Outlook-only table (inside <!--[if mso]>) that forces fixed column widths, while modern clients ignore it and use fluid inline-block divs.

<!--[if mso]><table role="presentation" width="600" cellpadding="0" cellspacing="0"><tr><td width="300"><![endif]-->
  <div style="display:inline-block;width:100%;max-width:300px;vertical-align:top;">Column A</div>
<!--[if mso]></td><td width="300"><![endif]-->
  <div style="display:inline-block;width:100%;max-width:300px;vertical-align:top;">Column B</div>
<!--[if mso]></td></tr></table><![endif]-->

VML bulletproof button

Outlook ignores padding/border-radius/background on an <a>, collapsing the click target to the text. VML draws a real, filled, fixed-size shape.

<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" href="https://ex.com"
  style="height:44px;width:200px;v-text-anchor:middle;" arcsize="12%" fillcolor="#B4652F" stroke="f">
  <center style="color:#fff;font-family:sans-serif;font-size:15px;">Shop now</center>
</v:roundrect>
<![endif]-->
<!--[if !mso]><!-->
<a href="https://ex.com" style="display:inline-block;padding:12px 28px;background:#B4652F;color:#fff;border-radius:6px;text-decoration:none;">Shop now</a>
<!--<![endif]-->

Part 3 β€” The Hero (Interview Level)

The fluid-hybrid ("spongy") pattern

Combine ghost tables (Outlook fixed widths) with max-width + display:inline-block divs. Result: responsive on modern clients, rigid on Outlook β€” and it still degrades to a sane single-column layout even when a client strips the media queries (Gmail app, some Outlook builds). This is the most robust responsive email architecture.

Dark mode CSS (and the client that ignores you)

<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<style>
  :root { color-scheme: light dark; }
  @media (prefers-color-scheme: dark) {
    .body  { background:#15161B !important; }
    .text  { color:#E9E7E2 !important; }
    .logo-light { display:none !important; }
    .logo-dark  { display:block !important; }
  }
  /* Outlook.com force-inversion overrides */
  [data-ogsc] .text { color:#E9E7E2 !important; }
</style>
Client Dark-mode behaviour Your defense
Apple Mail respects your CSS prefers-color-scheme
Gmail app (Android) force-inverts avoid pure-black-on-transparent logos
Outlook.com force-inverts [data-ogsc] / [data-ogsb] overrides
Windows Outlook ignores dark CSS design light to still read

Debugging checklist

  • Random vertical gaps under images β†’ set display:block + font-size:0/line-height:0 on containers.
  • Extra line spacing in Outlook β†’ mso-line-height-rule:exactly + mso-padding-alt.
  • Background image missing in Outlook β†’ VML <v:rect>/<v:fill> fallback.
  • Black logo disappears in dark mode β†’ give it a non-transparent bg or a light halo; provide a dark-mode swap.
L3 closing line: "I build fluid-hybrid so layout survives stripped media queries, I use ghost tables and VML because Outlook is the Word engine, and I design dark mode to degrade gracefully under forced inversion β€” because you can influence dark mode but never fully control every client."

I13 β€” Tutorial: Cross-Client QA (Zero β†’ Hero)

🎯 What you'll master: the Litmus vs Email on Acid workflow (same job, different names), accessibility checks that previews can't catch, and images-off fallbacks β€” the QA discipline that keeps defects out of production.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

The two tools are the same workflow

What you do Litmus Email on Acid
Client previews Litmus Previews Email Previews
Pre-send QA sweep Litmus Checklist Campaign Precheck
Code + live preview Litmus Builder Editor
Review / sign-off Litmus Proof Collaboration tools
Accessibility in Checklist strong accessibility checks
Spam / deliverability Spam testing Deliverability & inbox display
Your line if asked: "I've used Litmus for cross-client rendering across Outlook, Gmail, Apple Mail and mobile. Email on Acid's Campaign Precheck is the same pre-send sweep under a different name. The workflow transfers directly."

Part 2 β€” The Intermediate Application

The pre-send QA sequence (tool-agnostic)

1. DATA        verify audience + suppression/exclusion logic
2. RENDER      preview across Outlook, Gmail, Apple Mail, mobile
3. IMAGES-OFF  every image has meaningful alt + styled fallback
4. DARK MODE   check both schemes; logos survive inversion
5. LINKS       every href + UTM validated, no 404 / redirect loop
6. CONTENT     spelling, subject, preheader, personalization defaults
7. ACCESS      contrast, alt text, semantic order, real text not images
8. SEED SEND   send to a seed list, read on real devices
9. DEPLOY

Images-off fallbacks

Many clients block images by default, so an image-only email can arrive blank.

Fragile
<img src="hero.png">
<!-- blank when images are off -->
Resilient
<img src="hero.png" width="600"
  alt="20% off, this weekend only"
  style="background:#B4652F;color:#fff;
  font-size:20px;line-height:200px;">
<!-- alt shows, styled, on a colored bg -->
  • Live text over images for anything critical (offer, CTA) β€” text always renders.
  • Bulletproof buttons (VML + styled <a>) instead of image buttons β€” an image CTA vanishes with images off.

Part 3 β€” The Hero (Interview Level)

What previews cannot catch

Previews are static screenshots β€” they miss the failures that actually hurt: broken/expired links, wrong UTMs, spam-filter triggers, load time, and accessibility (contrast, alt text, reading order). Those need Checklist/Precheck-style validators plus a real seed send β€” not eyes on a thumbnail.

Accessibility checks that matter

Check Why How
lang attribute screen readers pick the right voice <html lang="en">
role="presentation" on layout tables stops SR reading table structure on every layout <table>
Alt text images-off + SR users meaningful, not "image1.png"
Color contrast β‰₯ 4.5:1 low-vision readability check text vs background
Real text, not text-in-images SR + images-off + translation live HTML text
Logical source order SR reads DOM order order matches visual intent

The production discipline

  • Root-cause every escaped defect. Any bug that reaches production earns a root-cause pass + a new line on the QA checklist so the same class can't recur β€” this is the single strongest QA answer you can give.
  • Seed lists per major client, refreshed β€” rendering engines change (Gmail promotions tab, new Outlook).
  • Automate the deterministic checks (links, alt-text presence, image weight) so humans spend attention on judgment, not clicking.
L3 closing line: "Previews prove it looks right; Checklist/Precheck and a seed send prove it works β€” links, spam, accessibility, images-off. And every production defect becomes a permanent checklist item, so QA gets stronger over time instead of repeating mistakes."

I14 β€” Tutorial: Advanced SQL & DBMS (Zero β†’ Hero)

🎯 What you'll master: window functions to solve Nth-highest-salary cleanly, the DELETE vs TRUNCATE architecture (and when each is production-safe), and B-Tree indexing that kills full-table scans.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

Command categories

  • DDL (CREATE, ALTER, DROP, TRUNCATE) β€” structure. DML (INSERT, UPDATE, DELETE, MERGE) β€” rows. DCL (GRANT, REVOKE) β€” permissions. TCL (COMMIT, ROLLBACK, SAVEPOINT) β€” transactions.

The joins in one screen

Join Returns
INNER rows matching in both
LEFT OUTER all left + matches (NULLs where none)
RIGHT OUTER all right + matches
FULL OUTER all rows from both
CROSS Cartesian product
SELF table joined to itself

Part 2 β€” The Intermediate Application

DELETE vs TRUNCATE vs DROP β€” the architecture

DELETE (DML)
DELETE FROM orders WHERE status='X';
- row-by-row, fully LOGGED
- supports WHERE, fires TRIGGERS
- ROLLBACK-able
- keeps identity counter
- slow on big tables
TRUNCATE (DDL)
TRUNCATE TABLE orders;
- deallocates PAGES, minimal log
- no WHERE, no triggers
- RESETS identity/auto-increment
- fast; rollback engine-dependent
- DROP also removes the STRUCTURE
Production-safe answer: "DELETE … WHERE β€” it's logged, scoped and rollback-able. I never TRUNCATE/DROP in production without a backup and change window, because in MySQL/Oracle they auto-commit and can't be undone (SQL Server can roll a TRUNCATE back inside a transaction)."

Window functions

ROW_NUMBER/RANK/DENSE_RANK number rows within a PARTITION by an ORDER BY; they differ only in tie handling.

salary ROW_NUMBER RANK DENSE_RANK
5000 1 1 1
5000 2 1 1
4000 3 3 2
3000 4 4 3

Part 3 β€” The Hero (Interview Level)

Nth highest salary β€” the interview classic

-- Nth highest DISTINCT salary (ties count once -> DENSE_RANK)
SELECT DISTINCT salary FROM (
  SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employees
) t WHERE rnk = :N;

-- Highest paid per department (partitioned)
SELECT * FROM (
  SELECT e.*, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
  FROM employees e
) x WHERE rn = 1;
If the top salary is tied, "2nd highest" diverges: DENSE_RANK=2 returns the next distinct value; RANK=2 returns nothing (it jumped 1,1,3). For "2nd distinct amount," DENSE_RANK is correct. Ask which they mean.

B-Tree indexing & killing the full-table scan

  • A B-Tree keeps keys sorted β†’ O(log n) seeks, ranges and ordered reads instead of an O(n) scan. Serves =, <, >, BETWEEN, ORDER BY, prefix LIKE 'abc%'.
  • Clustered = physical row order (one/table, leaf = data). Non-clustered = separate structure + row pointer (many/table).
  • Composite (a,b,c) obeys the left-prefix rule: usable for a, a,b, a,b,c β€” not b alone.
Query smell Why the index is skipped Fix
WHERE YEAR(dt)=2024 function on column = non-sargable range: dt>='2024-01-01' AND dt<'2025-01-01'
LIKE '%x' leading wildcard full-text index or restructure
filter on b of (a,b) violates left-prefix reorder index or add one
stale plan out-of-date statistics UPDATE STATISTICS / ANALYZE

ACID & the concurrency trap

  • ACID = Atomic, Consistent, Isolated, Durable. Isolation levels trade correctness for throughput:
  • Dirty read (uncommitted), Non-repeatable read (value changed), Phantom read (rows appeared). REPEATABLE READ still allows phantoms per the standard; only SERIALIZABLE prevents all three.
  • Deadlock: two txns each hold what the other needs β†’ the engine detects a wait-for cycle, rolls back a victim. Prevent by acquiring locks in a consistent order and keeping txns short.
L3 closing line: "I index for the predicates I actually run β€” because every index speeds reads but slows writes β€” I keep columns sargable so the B-Tree can seek, and I default to DELETE … WHERE for anything recoverable, reserving TRUNCATE for a full, backed-up reset."

I15 β€” Tutorial: OOP & CS Fundamentals (Zero β†’ Hero)

🎯 What you'll master: abstract class vs interface (when each, and the diamond), stack vs heap memory with GC mechanics, and virtual function tables β€” the L1/L2 fundamentals answered at a level that survives L3 follow-ups.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

The four pillars, crisply

  • Abstraction β€” hide complexity behind a contract (design; hides what). Encapsulation β€” hide data behind access modifiers (implementation; hides data).
  • Inheritance β€” "is-a" reuse. Polymorphism β€” one interface, many implementations.
  • Overloading = compile-time (declared type). Overriding = run-time (actual object).
One-liner: "Abstraction hides complexity; encapsulation hides data. One is design, one is implementation." And: overloading is chosen by the compiler from the declared type; overriding by the runtime from the actual object.

Part 2 β€” The Intermediate Application

Abstract class vs Interface

Dimension Abstract class Interface (Java 8+)
Instance state / fields βœ… yes + constructors only public static final constants
Method bodies concrete + abstract default/static (8+), private (9+)
Multiple inheritance ❌ one superclass βœ… implement many
Semantic "is-a" + shared code/state "can-do" capability
Use when subclasses share state + partial impl unrelated types share a capability
Abstract class β€” shared state
abstract class Shape {
  protected String id;          // state
  Shape(String id){ this.id=id; }
  abstract double area();       // must override
  String label(){ return id; }  // shared impl
}
Interface β€” capability
interface Drawable {
  void draw();                  // contract
  default void hint(){          // Java 8 default
    System.out.println("render me");
  }
}

The diamond problem

interface A { default String hi(){ return "A"; } }
interface B { default String hi(){ return "B"; } }
class C implements A, B {
  @Override public String hi(){ return A.super.hi(); }  // MUST disambiguate
}

Java bans two-class inheritance to avoid ambiguous state; conflicting interface defaults force explicit X.super.hi(). C++ solves its diamond with virtual inheritance (class D : virtual public Base).

Part 3 β€” The Hero (Interview Level)

Stack vs Heap & Garbage Collection

Stack
- method frames, local primitives,
  reference variables
- LIFO, per-thread, tiny & fast
- auto-freed when the frame pops
- overflow -> StackOverflowError
Heap
- every `new` object & array
- shared across threads
- GC-managed, larger, slower
- exhaustion -> OutOfMemoryError
In Person p = new Person(); the reference p is on the stack, the object is on the heap. GC frees by reachability (no reference chain from a GC root), which handles cycles that reference-counting leaks.
  • Generational GC: most objects die young β†’ cheap Young-gen minor GCs (Eden + survivors), rare Old-gen majors; mark-sweep-compact.
  • Leaks still happen in Java: ever-growing static collections, un-deregistered listeners, unclosed resources β€” the GC can't collect what's still referenced.

Virtual function tables (vtable / vptr)

class Shape { public: virtual double area() = 0; };   // pure virtual
class Circle : public Shape {
  double r;
public:
  Circle(double r): r(r) {}
  double area() override { return 3.14159 * r * r; }
};
Shape* s = new Circle(2.0);
s->area();   // dynamic dispatch: follow vptr -> Circle's vtable
C++ Java
Dispatch default static; opt in with virtual virtual by default
Mechanism per-class vtable, per-object vptr per-class method table
Optimization β€” JIT devirtualizes/inlines monomorphic calls
Destructor gotcha base dtor must be virtual (else UB/leak) no dtor; use AutoCloseable
L3 closing line: "C++ makes you opt into virtual dispatch and stores a vptr per object; Java is virtual by default and lets the JIT optimize the common case away. I program to interfaces, reach for an abstract class only when there's genuine shared state, and I know a Java 'leak' is really unintended reachability, not a GC failure."

I16 β€” Tutorial: Algorithmic Logic / DSA (Zero β†’ Hero)

🎯 What you'll master: the two Infosys signature problems end-to-end β€” the Bulb Switcher's O(NΒ²) β†’ O(1) math collapse, and Dynamic Programming for Edit Distance β€” with the narration that scores in a live-coding round.

In this module β€” 3 sections

  1. Part 1 β€” The Fundamentals
  2. Part 2 β€” The Intermediate Application
  3. Part 3 β€” The Hero (Interview Level)

Open each section below, or use Next ▶ (or the key) to move through them one at a time.

Part 1 β€” The Fundamentals

How the coding round is actually graded

A correct silent solution scores lower than a narrated near-miss. The rubric is: Clarify β†’ Brute force β†’ Optimize (name the trick + new complexity) β†’ Narrate every line + dry-run. Talk continuously; if you pause, say what you're weighing.

The two problems in one line each

  • Bulb Switcher β€” n bulbs OFF; on pass i toggle every multiple of i; how many are ON after n passes?
  • Edit Distance β€” minimum inserts/deletes/substitutions to turn string a into b (Levenshtein).

Part 2 β€” The Intermediate Application

Bulb Switcher β€” brute force first

def bulbs_bruteforce(n):
    state = [False] * (n + 1)          # 1-indexed
    for i in range(1, n + 1):          # pass i
        for j in range(i, n + 1, i):   # multiples of i
            state[j] = not state[j]    # toggle
    return sum(state)                  # O(n^2) time, O(n) space

Edit Distance β€” the DP table

def edit_distance(a, b):
    m, n = len(a), len(b)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0] = i          # delete-all base case
    for j in range(n+1): dp[0][j] = j          # insert-all base case
    for i in range(1, m+1):
        for j in range(1, n+1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1]                       # match: carry diagonal
            else:
                dp[i][j] = 1 + min(dp[i-1][j],               # delete
                                   dp[i][j-1],               # insert
                                   dp[i-1][j-1])             # substitute
    return dp[m][n]                             # O(m*n)
        ""   r   o   s
   ""    0   1   2   3
   h     1   1   2   3
   o     2   2   1   2
   r     3   2   2   2
   s     4   3   3   2
   e     5   4   4   3   <-- horse -> ros = 3

Part 3 β€” The Hero (Interview Level)

Bulb Switcher β€” the O(1) collapse

Bulb k is toggled once per divisor of k. Divisors come in pairs (d, k/d) β†’ even count β†’ OFF, except perfect squares where one divisor is unpaired (d == k/d) β†’ odd count β†’ ON. So the answer is the number of perfect squares ≀ n = floor(sqrt(n)).
Brute force O(nΒ²)
toggle a boolean array,
two nested loops,
then count the True cells
Optimized O(1)
import math
def bulbs(n):
    return int(math.isqrt(n))
# n=100 -> 10

Edit Distance β€” the optimizations they probe

  • Space O(mΒ·n) β†’ O(min(m,n)): each cell needs only the previous row and the current row so far β€” keep two 1-D arrays (prev, cur) and swap them each outer iteration.
  • Cost-model twist: if substitution costs 2 while insert/delete cost 1, substitution is never worth it (delete+insert = 2), so it reduces to Longest Common Subsequence: edits = m + n βˆ’ 2Β·LCS(a,b).
def edit_distance_1d(a, b):
    m, n = len(a), len(b)
    prev = list(range(n + 1))
    for i in range(1, m + 1):
        cur = [i] + [0]*n
        for j in range(1, n + 1):
            cur[j] = prev[j-1] if a[i-1]==b[j-1] else 1+min(prev[j],cur[j-1],prev[j-1])
        prev = cur
    return prev[n]                # O(m*n) time, O(n) space

The narration script (say this aloud)

Step What you say
Clarify "Empty strings? Case-sensitive? What's the max length β€” does O(mΒ·n) pass?"
Brute "NaΓ―ve recursion on each position branches 3 ways β†’ exponential."
Optimize "Overlapping subproblems β†’ memoize into a DP table, O(mΒ·n)."
Refine "Each row needs only the previous row β†’ drop to O(n) space."
Verify "Dry-run 'horse'β†’'ros': answer 3. Base cases fill the first row/column."
L3 closing line: "For Bulb Switcher I'd state the O(nΒ²) simulation, then collapse it: only perfect squares have an odd divisor count, so the answer is floor(sqrt(n)) in O(1). For Edit Distance I'd build the O(mΒ·n) DP, then reduce to O(n) space with rolling rows β€” and I'd narrate the whole arc, because that's what the round is grading."

I17 β€” Charter (Spectrum): Company Track

🎯 Why this matters: the Infosys interview is for the end-client Charter Communications β€” the company behind the Spectrum brand, one of the largest US broadband/cable/mobile/voice providers. A telecom of that scale runs enormous customer-communications volume β€” billing, outage and service alerts, technician-appointment reminders, Spectrum Mobile, retention β€” across exactly the email + SMS + voice/IVR stack this role is built on. This chapter turns "Software Developer & Communications Stack" into "how I'd build comms for Charter," plus the compliance layer (TCPA) that a telecom lives and dies by.

🧠 One-screen mental model

        WHY CHARTER NEEDS THIS ROLE

   CHARTER = Spectrum: Internet Β· TV Β· Mobile Β· Voice  (tens of millions of customers)
        β”‚
        β”œβ”€ TRANSACTIONAL comms  billing, payments, outages, appointments, OTP
        β”œβ”€ MARKETING comms      upsell (Mobile), win-back, onboarding
        └─ CHANNELS             Email (SES) Β· SMS/10DLC Β· Voice/IVR Β· Push (My Spectrum App)
                                        β”‚
                                 built on AWS (Charter is an AWS + GenAI shop)
                                        β”‚
                                 GOVERNED BY  TCPA Β· CAN-SPAM Β· 10DLC consent

Who Charter is, and why this role exists

Context: Charter Communications operates the Spectrum brand β€” internet, cable TV, Spectrum Mobile, and voice β€” serving tens of millions of US residential and business customers. Publicly, Charter is an AWS shop: it announced a strategic collaboration with AWS on generative AI to modernize software development (standardizing on GitLab Duo with Amazon Q Developer), and it invests heavily in digital self-service (the My Spectrum App) that reaches customers by their preferred channel β€” phone, chat, email or text.

Answer (what to know):

  • Charter's business generates massive transactional communication volume: every bill, payment, autopay notice, outage/service alert, technician-appointment reminder, activation and security OTP is a message that must arrive.
  • On top of that sits marketing/lifecycle comms: Spectrum Mobile upsell, onboarding, retention/win-back β€” the high-value growth area.
  • The role β€” Software Developer & Communications Stack β€” is about building and running the platform that renders and sends those messages: templating (Handlebars), the send/orchestration layer (AWS Pinpoint/SES + EventBridge/Lambda), and cross-channel delivery (SMS/RCS/IVR/email).
  • Charter being an AWS + GenAI organization is why the JD pairs Handlebars with Pinpoint rather than SFMC β€” and it's a strong thing to reference.
Say this: "For a provider at Spectrum's scale, communications aren't marketing garnish β€” they're core operations. An outage alert or an appointment reminder that doesn't arrive becomes a call-center cost and a churn risk. I'd treat this platform as production infrastructure with the same rigor as the network itself."

🧠 Memory map: Charter = Spectrum, AWS shop, enormous transactional + lifecycle comms across every channel. Hook: "Bills, outages, appointments, Mobile upsell β€” on AWS."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: Why would a cable/mobile operator invest so heavily in a dedicated communications stack? β€” Volume and stakes: tens of millions of customers Γ— multiple transactional events each = billions of messages, where a missed outage/appointment/payment notice directly drives call-center load and churn. The platform is a cost-and-retention lever, not a nice-to-have.
  • ↳↳ Deepest: Charter publicly standardized software dev on AWS + GenAI tooling β€” how does that shape how you'd pitch yourself? β€” Lean into AWS-native thinking (Pinpoint/SES/EventBridge/Lambda), mention comfort with AI-assisted development (Amazon Q/GitLab Duo-style workflows), and frame your SFMC background as a direct translation onto their AWS stack rather than a different world.

The communications a telecom actually sends β€” mapped to your skills

Scenario: "What kinds of communications would you build for a client like Charter?" This is where you connect the guide's 8 skills to Charter's real use cases.

Answer (use cases β†’ the skill that delivers them):

  • Billing & payments β€” bill-ready, autopay receipt, payment-failed/past-due, paperless-billing β†’ transactional email (SES) + SMS; personalized with Handlebars from a pre-built context.
  • Outage & service alerts β€” proactive "we see an issue in your area / it's resolved" β†’ event-driven (EventBridge/Lambda) SMS + push; time-critical, so idempotent and fast.
  • Technician appointments β€” confirmation, day-before reminder, "tech en route", reschedule β†’ SMS + IVR, with reply-to-confirm.
  • Onboarding/activation β€” welcome, self-install guidance, app adoption β†’ email journeys + push.
  • Spectrum Mobile & upsell β€” line-add offers, device promos, win-back β†’ marketing comms (consent-gated).
  • Security β€” OTP / fraud alerts β†’ SMS/voice, read digit-by-digit (SSML).
The senior framing: separate transactional from marketing. Transactional (outage, OTP, appointment) must always deliver and honor only hard suppressions; marketing (Mobile upsell) is consent-gated and honors every opt-out. Conflating them is both a UX and a legal failure (see TCPA next).

🧠 Memory map: Map each Charter event to a channel + the guide skill that ships it; split transactional from marketing. Hook: "Outage=event SMS, bill=Handlebars email, OTP=SSML voice, upsell=consented."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: An outage alert and a Mobile upsell are both "an SMS" β€” why build them completely differently? β€” Different classification, latency and consent: the outage is transactional, real-time (EventBridgeβ†’Lambdaβ†’send), and legally sendable to all affected customers; the upsell is marketing, batched/segmented, and may only go to customers with prior express consent. Same channel, different pipeline and rules.
  • ↳↳ Deepest: For proactive outage alerts at scale, what's the hardest engineering problem? β€” Fan-out + idempotency + suppression under a spike: an outage hits thousands of endpoints at once, so you need a queue/back-pressure (SQS), dedupe so a flapping outage doesn't re-blast, and correct who's-actually-affected targeting β€” a wrong-audience outage blast is worse than silence.

TCPA & telecom compliance β€” the layer that decides everything

Scenario: "How do you keep Charter's SMS and voice campaigns compliant?" For a telecom this is not optional trivia β€” it's the constraint every design bends around.

Answer:

  • TCPA (Telephone Consumer Protection Act) governs calls and texts. Marketing SMS/autodialed or prerecorded voice requires prior express written consent; purely transactional/informational messages need prior express consent but not written.
  • Consent is revocable by any reasonable means β€” a "STOP" reply, a call, an email β€” and revocation must propagate across channels quickly.
  • Quiet hours: no marketing calls/texts outside 8am–9pm local to the recipient.
  • 10DLC β€” US A2P SMS must run on registered brand + campaign (message content, opt-in flow) or carriers filter it.
  • CAN-SPAM for email β€” accurate From/subject, physical address, working unsubscribe honored promptly.
  • Engineering consequence: consent/preference state is a first-class data model and a hard gate at send time β€” you never rely on the campaign author to remember.
The line that reads as senior: "At a telecom, consent is an architectural concern, not a checkbox. I'd model channel-level consent and revocation as suppression that's enforced at send time, propagated across channels, and auditable β€” because a TCPA violation is per-message statutory damages at Spectrum's volume."

🧠 Memory map: TCPA = consent for marketing texts/calls, revocable anytime, quiet hours, 10DLC registered; enforce consent as a send-time gate. Hook: "Consent is architecture: gate every marketing send, honor STOP everywhere."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: A customer replies STOP to a marketing text but still needs outage alerts. How do you handle it? β€” Model consent per channel and per message-category: STOP suppresses marketing on SMS, but transactional/service alerts (outage, security) are a separate lawful basis and continue. One global flag is wrong β€” you need category-scoped suppression.
  • ↳↳ Deepest: How do you make consent revocation reliable across email, SMS and voice at scale? β€” A central consent/preference service as the source of truth that every channel checks at send time, updated by every opt-out path (STOP, unsubscribe link, call-center, app), with low propagation latency and an audit log. Channel-local opt-out lists drift and cause violations; centralize and gate.

Likely Charter scenario questions (with model answers)

Scenario: telecom-flavored versions of the scenarios you've drilled β€” expect these shaped around Spectrum's world.

Answer (three they may throw, answered briefly):

  • "Design proactive outage notifications." β†’ Event from the network monitoring system β†’ EventBridge rule β†’ Lambda resolves affected endpoints from the address/account model β†’ SMS + push via Pinpoint, idempotent (dedupe on outage-id + endpoint) with SQS back-pressure for the spike; suppress marketing, allow service class; send an "all-clear" on resolve.
  • "A bill-ready email renders wrong in Outlook for some customers." β†’ It's the Word engine: check unsupported CSS, switch to table + ghost-table layout, VML button; but also verify the Handlebars context β€” a missing key (e.g. amountDue) is the more common "some customers" cause than rendering.
  • "Appointment reminders aren't reaching some customers." β†’ Split never-sent vs failed: check consent/suppression (did they opt out of SMS?), 10DLC campaign health/carrier filtering, invalid/rotated numbers, then the event trigger itself β€” prove it with send + delivery logs, not guesses.
Every Charter scenario answer should end the way a lead's does: classification (transactional vs marketing) β†’ design β†’ failure thinking β†’ how I'd verify. The telecom twist is that "who is it legal and appropriate to send this to?" is always part of the design.

🧠 Memory map: Outage = event-driven + idempotent + back-pressure; render bug = Word engine and check the context; not-delivered = never-sent vs failed via logs. Hook: "Classify, design, fail-think, verify β€” and always ask who it's legal to send to."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: For outage alerts, how do you avoid alert fatigue when an outage flaps up and down? β€” Debounce at the source (confirm the outage is stable for N minutes before notifying), dedupe per outage-id, and cap notifications per customer per window β€” so a flapping node doesn't send ten messages.
  • ↳↳ Deepest: Millions of customers, a regional outage β€” how do you send fast without melting SES/SMS throughput or tripping rate limits? β€” Pre-resolve the affected segment, enqueue to SQS, drain with a concurrency-controlled worker pool sized to your SES/10DLC throughput, use template + bulk send APIs, and monitor bounce/throttle to back off β€” turning a spike into a fast-but-controlled drain instead of failures.

How to frame your experience for Charter + questions to ask

Scenario: the opening and closing of the interview, tuned to Charter.

Answer:

  • Frame your SFMC/comms background as a direct fit: you've built high-volume transactional and lifecycle messaging (email/SMS/multichannel) with templating, personalization and cross-client QA β€” exactly Charter's problem, and the AMPscriptβ†’Handlebars / SFMCβ†’Pinpoint mapping is a vocabulary switch, not a relearn.
  • Reference their world: Spectrum's scale, the transactional-vs-marketing split, TCPA discipline, and their AWS + GenAI direction β€” it shows you researched the client, not just the JD.
  • Use the acknowledge β†’ map β†’ steer move on any gap (see the Execution chapter).
  • Smart questions to ask them:
  • "Is the comms platform Pinpoint/SES-native, or a mix with an existing ESP?"
  • "How is consent/preference managed today across email, SMS and voice β€” one service or per-channel?"
  • "What's the split between transactional (outage/billing/appointments) and marketing volume?"
  • "Where does the team spend most time β€” templating/build, orchestration, or deliverability?"
Closing positioning: "My depth is the exact problem Charter has β€” reliable, personalized, compliant messaging at scale. The stack names differ from where I built it, but the two jobs β€” templating and cross-channel delivery β€” are identical, and I've already mapped them onto Charter's AWS-based stack."

🧠 Memory map: Fit = same problem (comms at scale); reference Spectrum scale + TCPA + AWS; ask about stack, consent, and the transactional/marketing split. Hook: "Same problem, their vocabulary β€” and I did my homework on Charter."

🎯 Drill deeper (the follow-ups they'll ask):

  • ↳ Deeper: "You've used SFMC, not our stack β€” why should we trust you here?" β€” "Because the hard parts transfer: high-volume deliverability, personalization, cross-client QA and consent discipline are stack-independent. The tool-specific syntax β€” Handlebars, Pinpoint β€” I've already mapped from AMPscript and SFMC; that's the fast part to pick up."
  • ↳↳ Deepest: What one question, asked in the interview, signals you think like an owner of Charter's comms platform? β€” "How is consent and preference managed across channels today?" β€” it shows you understand that at a telecom the platform's hardest, highest-stakes problem is compliant, cross-channel consent, not templating β€” which is exactly what a lead worries about.

I18 β€” Advanced Email Dev Lab: Outlook, VML & Handlebars

🎯 What you'll master: the exact, copy-pasteable snippets an L2/L3 Email Developer uses in a live coding interview or on the job β€” ghost tables, VML backgrounds and buttons, web-font fallbacks, dark mode, the Outlook 120-DPI fix, and hands-on Handlebars. Every skill = the Fundamentals (the why) and the Hero (the production code).

🧠 One-screen mental model

        THE OUTLOOK SURVIVAL KIT (Windows Outlook = MS Word engine)

   LAYOUT      ghost tables  <!--[if mso]> ... <![endif]-->
   BACKGROUND  VML  <v:rect> + <v:fill type="frame">
   BUTTON      VML  <v:roundrect arcsize> + <w:anchorlock/>
   FONTS       web font for modern clients; force Arial for mso
   DARK MODE   color-scheme meta + prefers-color-scheme + [data-ogsc]
   120 DPI     <o:PixelsPerInch>96</o:PixelsPerInch>
   DATA        Handlebars: {{#each}}, {{@last}}, helpers, {{{raw}}}

Skill 1 β€” Email breakage & Ghost Tables

The Fundamentals β€” why emails break in Outlook

  • Windows Outlook (2007–2021 + the classic client) renders with the Microsoft Word engine, not a browser.
  • Consequence: no max-width, no flexbox, no grid, no float, no negative margins, no background shorthand. A max-width container simply expands full-width in Outlook.
  • The fix pattern: give Outlook a fixed-width table it does understand, while modern clients use a fluid max-width div β€” the two coexist via a conditional comment.
Interviewer trap: "Your max-width:600px centered layout is full-bleed in Outlook β€” why?" The Word engine ignores max-width. The answer is a ghost table: an Outlook-only fixed-width table that props the layout open.

The Hero β€” the ghost table

<!--[if mso]>
<table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0">
<tr><td width="600">
<![endif]-->
<div style="max-width:600px; margin:0 auto;">
  <!-- fluid, responsive content for every modern client -->
</div>
<!--[if mso]>
</td></tr></table>
<![endif]-->

Two-column that stacks on mobile but stays side-by-side in Outlook:

<!--[if mso]><table role="presentation" width="600"><tr><td width="300" valign="top"><![endif]-->
<div style="display:inline-block; width:100%; max-width:300px; vertical-align:top;">Column A</div>
<!--[if mso]></td><td width="300" valign="top"><![endif]-->
<div style="display:inline-block; width:100%; max-width:300px; vertical-align:top;">Column B</div>
<!--[if mso]></td></tr></table><![endif]-->

Skill 2 β€” VML Background Images for Outlook

The Fundamentals β€” the scenario

  • A hero section needs a background image with HTML text overlaid (headline + CTA on top of a photo).
  • Standard CSS background-image (and background shorthand) does not render in Windows Outlook β€” you get a blank or solid-color block.
  • The fix is VML (Vector Markup Language) β€” Office's own drawing layer β€” with type="frame" to scale the image, and a <v:textbox> to hold the real HTML overlay.

The Hero β€” VML <v:rect> + <v:fill> (Outlook 2007–2021)

<div style="background-color:#222222; background-image:url('https://cdn.example.com/hero.jpg');
            background-size:cover; background-position:center;">
<!--[if mso]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
        fill="true" stroke="false" style="width:600px; height:320px;">
  <v:fill type="frame" src="https://cdn.example.com/hero.jpg" color="#222222" />
  <v:textbox inset="0,0,0,0">
<![endif]-->
  <div>
    <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0">
      <tr><td align="center" style="padding:80px 20px;">
        <h1 style="color:#ffffff; font-family:Arial,sans-serif; margin:0;">Summer Sale</h1>
      </td></tr>
    </table>
  </div>
<!--[if mso]>
  </v:textbox>
</v:rect>
<![endif]-->
</div>
Traps: the VML width/height must be fixed pixels (no %); type="frame" gives cover-style scaling; always set a color= fallback and a CSS background-color so images-off still reads. Text lives in the <v:textbox>, mirrored by the normal HTML for other clients.

Skill 3 β€” Bulletproof Buttons (VML roundrect)

The Fundamentals β€” the scenario

  • Outlook ignores border-radius, padding and background on an <a>, so a CSS button renders square and the click target shrinks to the text.
  • Fix: draw a real VML <v:roundrect> with arcsize (the rounded corners), <w:anchorlock/> (locks the text so Outlook can't shift it), and a <center> for text β€” behind a normal styled <a> for every other client.

The Hero β€” VML roundrect + <w:anchorlock/>

Standard (square in Outlook)
<a href="https://ex.com" style="
  background:#B4652F; border-radius:6px;
  color:#fff; padding:14px 28px;
  text-decoration:none;">Shop Now</a>
Bulletproof (rounded everywhere)
<!--[if mso]>
<v:roundrect
  xmlns:v="urn:schemas-microsoft-com:vml"
  xmlns:w="urn:schemas-microsoft-com:office:word"
  href="https://ex.com" arcsize="12%"
  fillcolor="#B4652F" strokecolor="#B4652F"
  style="height:48px;width:220px;
         v-text-anchor:middle;">
  <w:anchorlock/>
  <center style="color:#fff;
    font-family:Arial,sans-serif;
    font-size:16px;font-weight:bold;">
    Shop Now</center>
</v:roundrect>
<![endif]-->

Complete, both paths (Outlook + everyone else):

<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
  href="https://ex.com" arcsize="12%" fillcolor="#B4652F" strokecolor="#B4652F"
  style="height:48px; width:220px; v-text-anchor:middle;">
  <w:anchorlock/>
  <center style="color:#ffffff; font-family:Arial,sans-serif; font-size:16px; font-weight:bold;">Shop Now</center>
</v:roundrect>
<![endif]-->
<!--[if !mso]><!-->
<a href="https://ex.com" style="background-color:#B4652F; border-radius:6px; color:#ffffff;
   display:inline-block; font-family:Arial,sans-serif; font-size:16px; font-weight:bold;
   line-height:48px; text-align:center; text-decoration:none; width:220px;
   -webkit-text-size-adjust:none;">Shop Now</a>
<!--<![endif]-->
The details that matter: arcsize="12%" β‰ˆ the CSS border-radius (as a % of height); v-text-anchor:middle + <w:anchorlock/> vertically center and lock the label; match the VML height/width to the <a>'s line-height/width so both clients look identical.

Skill 4 β€” Custom Web Fonts & the Outlook "Times New Roman" bug

The Fundamentals β€” the scenario

  • Brand wants a custom font (Google Fonts or self-hosted). Web fonts work in Apple Mail, iOS Mail and some Samsung/Outlook-app clients β€” but not Gmail, not Windows Outlook, not Outlook.com.
  • The bug: in Windows Outlook, when a font-family stack starts with a web font it doesn't recognize (e.g. 'Poppins', Arial, sans-serif), Outlook doesn't fall through to Arial β€” it silently defaults the whole thing to Times New Roman. Your clean sans-serif email arrives looking like a legal letter.
<!-- 1) Load the web font for capable clients only (hidden from Outlook/mso) -->
<!--[if !mso]><!-->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
</style>
<!--<![endif]-->

<!-- Self-hosted alternative: -->
<style>
  @font-face {
    font-family: 'BrandSans';
    font-style: normal; font-weight: 400;
    src: url('https://cdn.example.com/fonts/brandsans.woff2') format('woff2');
    mso-font-alt: 'Arial';   /* hint Outlook toward a safe substitute */
  }
</style>

<!-- 2) THE FIX: force Outlook to Arial so it never falls to Times New Roman -->
<!--[if mso]>
<style>
  * { font-family: Arial, sans-serif !important; }
</style>
<![endif]-->

Then use the stack normally β€” modern clients get Poppins, Outlook gets Arial (not Times):

<td style="font-family:'Poppins', Arial, Helvetica, sans-serif; font-size:16px;">Body copy</td>
Interviewer trap: "You set a fallback stack β€” why is Outlook showing Times New Roman?" Because Outlook ignores the fallback when the first font is a web font. The fix is the <!--[if mso]> block that hard-sets font-family:Arial !important, plus mso-font-alt on the @font-face. Always name a web-safe font second in the stack regardless.

Skill 5 β€” Media Queries & Dark Mode (prefers-color-scheme)

The Fundamentals β€” the scenario

  • The email looks right in light mode but Gmail / Outlook (iOS & desktop) aggressively invert colors in dark mode β€” black-on-white becomes muddy, and dark logos vanish on dark backgrounds.
  • You declare dark-mode support so clients that respect your CSS use your palette, and you add mitigations for the clients that force-invert anyway.

The Hero β€” meta tags + media query + logo swap + Outlook.com overrides

<head>
  <meta name="color-scheme" content="light dark">
  <meta name="supported-color-schemes" content="light dark">
  <style>
    :root { color-scheme: light dark; supported-color-schemes: light dark; }

    /* Clients that respect author dark styles */
    @media (prefers-color-scheme: dark) {
      .email-bg   { background:#1a1a1a !important; }
      .email-card { background:#242424 !important; }
      .email-text { color:#eaeaea !important; }
      .dark-logo  { display:block !important; width:160px !important; max-height:inherit !important; }
      .light-logo { display:none !important; }
    }

    /* Outlook.com dark mode uses [data-ogsc]/[data-ogsb] attribute hooks */
    [data-ogsc] .email-text { color:#eaeaea !important; }
    [data-ogsb] .email-bg   { background:#1a1a1a !important; }
  </style>
</head>

Logo swap in the body (both present; CSS shows the right one):

<img src="logo-light.png" class="light-logo" width="160" alt="Brand" style="display:block;">
<div class="dark-logo" style="display:none; mso-hide:all;">
  <img src="logo-dark.png" width="160" alt="Brand" style="display:block;">
</div>
Traps & mitigations: Gmail app and Outlook.com force-invert regardless β€” so never put a pure-black logo on a transparent PNG (add a subtle background or light outline), and test both schemes. You can influence dark mode; you cannot fully control every client β€” design to degrade gracefully under forced inversion.

Skill 6 β€” QA Tools & the Outlook 120-DPI Bug

The Fundamentals β€” the scenario

  • A client reports the email is broken on "Outlook 120 DPI." On Windows displays scaled to 120/144 DPI, Outlook multiplies dimensions (Γ—1.25 / Γ—1.5) β€” but only for elements without explicit sizing β€” so images blow up, columns misalign, and VML shifts.
  • Root causes: relying on CSS height, missing image width/height, and percentage widths inside Outlook.

The Hero β€” the one-line DPI fix + the QA workflow

Force Outlook to render at 96 DPI regardless of Windows scaling (put in <head>):

<!--[if mso]>
<xml>
  <o:OfficeDocumentSettings>
    <o:AllowPNG/>
    <o:PixelsPerInch>96</o:PixelsPerInch>
  </o:OfficeDocumentSettings>
</xml>
<![endif]-->

Plus the rules that keep 120 DPI stable:

  • Set both width and height as HTML attributes on every <img> (not just CSS).
  • Give VML elements explicit px dimensions matching their HTML.
  • Avoid CSS height on <td>; use padding for vertical space.

How to isolate it with Litmus / Email on Acid:

  • Reproduce: open the render in the Outlook 120 DPI preview client in Litmus Previews / Email on Acid Email Previews to see the exact break.
  • Iterate: edit in Litmus Builder (code + live multi-client preview) and re-check only the Outlook DPI variants.
  • Pre-send sweep: run Litmus Checklist / EoA Campaign Precheck for images-off (every image needs meaningful alt + styled fallback), link/URL validation, spam scoring, and accessibility (screen-reader order, contrast, role="presentation" on layout tables, lang attribute).
  • Sign off: seed-send and read on a real device before deploy.
Interviewer trap: "It's fine on my Outlook but broken on the client's" β€” that's display scaling (DPI). Lead with the <o:PixelsPerInch>96</o:PixelsPerInch> fix and explicit image dimensions, then confirm across the DPI preview matrix β€” don't debug on one machine.

Skill 7 β€” Handlebars.js Hands-On Scenarios

The Fundamentals β€” logic-less rendering

  • Handlebars renders a pre-built JSON context and cannot fetch data or run comparisons β€” logic lives upstream. {{ }} HTML-escapes; {{{ }}} is raw. {{#each}} iterates; {{@last}}/{{@first}}/{{@index}} are loop metadata.

The Hero β€” Scenario 1: loop a cart, detect the last item

// upstream context
const ctx = { cart: [
  { name: "Router",  priceCents: 12900 },
  { name: "Modem",   priceCents:  8900 },
  { name: "Cable",   priceCents:  1500 }
]};
<table role="presentation" width="100%">
  {{#each cart}}
  <tr>
    <td>{{this.name}}</td>
    <td align="right">{{money this.priceCents}}</td>
  </tr>
  {{#unless @last}}
  <tr><td colspan="2" style="border-bottom:1px solid #eee; font-size:0; line-height:0;">&nbsp;</td></tr>
  {{/unless}}
  {{/each}}
</table>

{{#unless @last}} draws a divider between rows but not after the final one β€” the classic @last use.

The Hero β€” Scenario 2: register helpers (compare + currency)

// Comparison block helper β€” because {{#if}} can't do ==
Handlebars.registerHelper('eq', function (a, b, options) {
  return a === b ? options.fn(this) : options.inverse(this);
});
// Inline formatting helper (presentation only)
Handlebars.registerHelper('money', cents => '$' + (cents / 100).toFixed(2));
{{#eq currentTier "Gold"}}
  <p>Your Gold perks are ready.</p>
{{else}}
  <p>Upgrade to Gold for free shipping.</p>
{{/eq}}
Order total: {{money order.amountCents}}

The Hero β€” Scenario 3: raw HTML safely ({{{ }}} vs {{ }})

Double β€” escaped (safe default)
{{cmsBlock}}
<!-- <b>Hi</b> renders as
     literal text: &lt;b&gt;Hi... -->
Triple β€” raw (audited only)
{{{cmsBlock}}}
<!-- injects markup verbatim;
     XSS risk if not sanitized -->
// Sanitize ONCE, upstream, before it ever enters the context
import DOMPurify from 'isomorphic-dompurify';
ctx.cmsBlock = DOMPurify.sanitize(rawCmsHtml, { ALLOWED_TAGS: ['b','i','a','p','br','ul','li','strong','em'] });
Interviewer trap: "When do you use triple braces?" Only for trusted, pre-sanitized markup (a CMS block cleaned upstream). Any user-supplied string in {{{ }}} is stored XSS. Default to {{ }}; treat {{{ }}} as a deliberate, audited exception. And remember escaping protects the HTML body β€” a value in an href or inline onclick needs context-specific encoding on top.

β˜… Marked for Review

Sections you flagged with the β˜† Mark button in the bar above (or the M key) while studying. Click any item to jump straight back to it. This list is saved in your browser and updates automatically.