Journal: Prompt Engineering Done, LangChain Started, and Finally Cracking LeetCode 42
Full-stack Engineer specializing in Node.js, Nest.js, MERN. Expert in building scalable APIs & real-time apps. Focus on clean code, Security, and performance.
Wrapping Up the Prompt Engineering Course
Finished the ChatGPT Prompt Engineering for Developers course on deeplearning.ai today — or at least got close enough to call it done. It's a beginner-level course, but honestly it had a few good reminders worth writing down.
The core message of the whole thing boiled down to two rules: be as specific as possible, and give the model time to think. Everything else kind of builds on top of those two ideas.
For being specific, the course pushes you to:
Use delimiters to structure your prompts
Provide the output format
Add conditions so you can verify whether the output actually satisfies what you were looking for
Simple stuff on paper, but easy to forget when you're just throwing things at a model hoping something sticks.
The "give the model time to think" part was about breaking your prompt into explicit steps — basically treating the model like it needs a recipe, not a vague instruction. Specify each step, ask for output in a defined shape, and let the model work through it rather than expecting a magic one-liner to do everything.
Beyond the prompting basics, the course walked through a handful of practical use cases: iterative prompting (start rough, refine gradually), summarizing content, inferring meaning, transforming text, expanding ideas from a seed concept, and finally building a chatbot. The chatbot section was probably the most useful — it showed how to use a list of messages with a system prompt at the start, then alternating user and assistant turns, to maintain context across a conversation.
Next Up: LangChain for LLM App Development
Right after finishing the prompt engineering course, I jumped straight into the LangChain for LLM Application Development course. Haven't gotten far yet, but that's where the focus is moving next.
LeetCode 42 — Trapping Rain Water
Took a break from courses and worked on a LeetCode problem. The problem gives you an array of heights and asks how many units of water the elevation map can trap between the bars after it rains. Classic.
For example: [1, 0, 2] can trap one unit of water in the dip at index 1. The goal is to figure out the total across the whole array.
My first instinct was to go with a two-pointer setup and find the next greater element using a monotonic stack. The problem is, I couldn't remember exactly how the monotonic stack worked. I tried to reconstruct it from memory — empty array for the stack, another array for the output, fill with the same length as the input — but I got fuzzy pretty quickly.
I attempted it anyway. The rough idea was: for every element, if the current number is smaller than the top of the stack, pop it and push the new number. That gets rid of any smaller numbers and leaves you with the next greater elements. In theory. In practice, I was going in circles and not actually extracting anything useful.
First Real Attempt — Next Greater Element from Both Sides
I stepped back and thought about it differently. The idea was to find the next greater element going left to right, and then separately going right to left. For every index, the water it can hold is determined by the minimum of those two "walls" minus the current height at that position.
// Pseudocode sketch
for each index i:
leftMax[i] = max height to the left of i
rightMax[i] = max height to the right of i
minWall = min(leftMax[i], rightMax[i])
if minWall > height[i]:
water += minWall - height[i]
I coded it up, but my output was off — expected 5, got 6, didn't match. The indexing or the boundary conditions were wrong somewhere. I decided to look at the algorithm more carefully instead of guessing.
Prefix Max Array Approach — The One That Actually Worked
After reviewing, I found two clean approaches. Started with the prefix sum (or prefix max) array method.
The idea is straightforward: precompute an array leftMax where leftMax[i] is the highest bar seen so far from the left up to (but not including) i. Then do the same from the right with rightMax[i]. Once you have both, loop through every index and compute the water.
For the left pass, start from index 1 and at each step take the max of leftMax[i-1] and height[i-1]. For the right pass, start from index height.length - 2 and move backwards, taking the max of rightMax[i+1] and height[i+1].
I logged leftMax and rightMax first just to sanity-check they were right, and they looked good. Then I added the water calculation — only add if minimum - currentHeight > 0, otherwise skip. Submitted. It worked.
The final code
function trap(height: number[]): number {
const leftMax = new Array(height.length).fill(0)
for (let i = 1; i < height.length; i++) {
leftMax[i] = Math.max(leftMax[i - 1], height[i - 1])
}
const rightMax = new Array(height.length).fill(0)
for (let i = height.length - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], height[i + 1])
}
let water = 0
for (let i = 0; i < height.length; i++) {
const min = Math.min(leftMax[i], rightMax[i])
if (min - height[i] < 0) continue
water += min - height[i]
}
return water
};
Monotonic Stack Approach — The Hard Way (and also working now)
With the prefix sum solution accepted, I went back and made myself figure out the monotonic stack version properly.
The stack stores indices, not heights. You loop through every height, and before pushing the current index, you check: if the stack isn't empty and the current height is greater than the height at the top of the stack, that means you've found a "bowl" — a dip that can hold water.
When that happens:
Pop the top of the stack — that's the bottom of the bowl.
If the stack is now empty, there's no left wall, so break out of the while loop.
The left wall is whatever is now at the top of the stack.
The right wall is the current index.
Compute the width:
currentIndex - leftIndex - 1Compute the effective height:
min(height[left], height[current]) - height[bottom]Water trapped in this segment:
height * widthAdd to total and keep looping.
The -1 on the width tripped me up for a bit. If the left wall is at index 3 and the right wall is at index 5, the raw difference is 2, but there's only 1 cell in between (index 4). So you always subtract 1.
function trap(height: number[]): number {
let water = 0
const stack = []
for (let i = 0; i < height.length; i++) {
while (stack.length && height[i] > height[stack.at(-1)]) {
const gap = stack.pop()
if (!stack.length) break
const left = stack.at(-1)
const width = i - left - 1
const boundHeight = Math.min(height[left], height[i]) - height[gap]
water += boundHeight * width
}
stack.push(i)
}
return water
};
The while loop runs inside the for loop, and the stack push happens after the while loop exits. That's the rhythm of the whole thing.
What I Took Away Today
The prompt engineering course was a good foundation even if it felt basic — the iterative prompting section especially. For the LeetCode problem, the prefix sum approach is the easier one to reason about; the monotonic stack is more efficient in some ways but demands clean mental model to avoid off-by-one bugs. Got both working, which felt like a good ending to a long day.

