Blok 8 · Ders 55
Mülakata hazırlık ve sonrası
Sık sorulan JavaScript, React ve SQL soruları, canlı kodlama, kendi projeni anlatmak, ev ödevi ve öğrenmeye devam yol haritası.
Bu dersin sonunda
- Junior işe alım sürecinin aşamalarını sayıp her birinde neyin ölçüldüğünü ve nasıl hazırlanacağını söyleyebilirsin
var/let/const,==/===, closure, olay döngüsü, Promise,this, değer ve referans sorularını iki üç cümle ve çalışan bir örnekle cevaplayabilirsin- React'te state ve props,
keyve effect; SQL'de JOIN türleri, indeks ve normalizasyon sorularını kısa ve doğru cevaplayabilirsin - Canlı kodlamada bir problemi sesli düşünerek çözebilirsin: soruyu netleştir, örnekleri ve sınır durumlarını yaz, planla, kodla, dene
- Bir projeni problem, çözüm, zorluk ve öğrendiğin sırasıyla iki dakikada anlatabilir, bilmediğin bir soruya dürüst ve yol gösteren bir cevap verebilirsin
- Ev ödevini README'si, testleri ve okunur commit geçmişiyle teslim edebilir, işe girdikten sonraki ilk 90 gününü ve öğrenme yol haritanı çıkarabilirsin
Bu sayfada
Kursun son dersine geldin. Öğrendiklerini bu kez bir yabancıya, kısa sürede ve biraz heyecanla göstermeyi çalışacaksın: mülakat. Önce bir işe alım sürecinin aşamalarını tanıyacaksın. Sonra junior mülakatlarında en sık gelen JavaScript, React ve SQL sorularını kısa ve doğru cevaplarıyla, her birini çalışan bir örnekle geçeceksin. Ardından canlı kodlamada sesli düşünmeyi, bir projeni anlatmayı, bilmediğin soruya cevap vermeyi ve ev ödevi teslim etmeyi öğreneceksin. En sonda işe girdikten sonraki ilk üç ay ve öğrenmeye nasıl devam edeceğin var.
Bu dersin amacı cevap ezberletmek değil. Mülakatçı, ezberlenmiş bir tanımı ilk ek soruda fark eder. Buradaki her cevabı bir kez kendin çalıştırıp kendi sözlerinle söyleyebildiğinde hazırsın.
Mülakat iki yönlü bir görüşmedir
Bir junior ilanına başvuran adaydan her şeyi bilmesi beklenmez. Mülakatçı üç şeyi anlamaya çalışır: Temelleri sağlam mı? Takılınca ne yapıyor? Onunla birlikte çalışmak nasıl olur? Bu üçünün hiçbiri "bütün soruları doğru bildi" ile ölçülmez. Bir soruyu bilmeyip nasıl bulacağını açıkça anlatan aday, ezberini okuyup ilk ek soruda susan adaydan çoğu zaman daha iyi izlenim bırakır.
Görüşme senin için de bir sınavdır: o ekipte öğrenebilecek misin, sana kim yardım edecek, iş tarifi ilandakiyle aynı mı? Bunları sormak saygısızlık değil, ciddiyet işaretidir. Sorulacak sorular kültür görüşmesi bölümünde.
Mülakat aşamaları
Şirketten şirkete değişse de bir junior işe alım süreci çoğunlukla şu sırayla ilerler:
Süreler ve sıra yalnızca fikir versin diye var. Bazı şirketler canlı kodlamanın yerine otomatik puanlanan bir çevrim içi test gönderir, bazıları teknik görüşmeyle canlı kodlamayı aynı saate sığdırır. Sürecin nasıl ilerleyeceğini ön görüşmede sormak en kolay yoldur: "Süreçte sırada hangi adımlar var?"
Ön görüşme
İlk konuşma genelde işe alım uzmanıyladır ve tekniğe pek girmez. Amaç senin ilana uyup uymadığını, beklentilerinizin örtüşüp örtüşmediğini görmektir.
Sık gelen sorular: "Kendinden bahseder misin?", "Neden yazılım?", "Neden bizim şirketimiz?", "Maaş beklentin nedir?", "Ne zaman başlayabilirsin?". İlkine 30–45 saniyelik, önceden çalışılmış bir cevap hazırla. Köşeli parantezli yerleri kendi bilgilerinle doldur:
Ben [adın]. [Süre] boyunca kendikendime yazılım öğrendim; TypeScript,React, Node.js ve PostgreSQL ile tamyığın projeler yaptım. En sevdiğimproje [proje]: [bir cümlede neyaptığı]. Şu an ilk junior işimiarıyorum. Sizin ilanınızda [ilandakisomut bir şey] dikkatimi çekti, çünkü[kısa sebep].Maaş sorusuna hazırlıksız yakalanma: benzer ilanlara ve kaynağı ile tarihi belli güncel maaş araştırmalarına bakıp bir aralık belirle (Ders 54'te maaş rakamlarının neden çok değiştiğini ve hızla eskidiğini konuştuk). İlk konuşmada kesin rakam vermek zorunda değilsin; "Rolün kapsamını öğrendikten sonra konuşmak isterim, araştırmama göre şu aralıktayım" demek normaldir.
Teknik görüşme ve canlı kodlama
Teknik görüşmede ekipten bir yazılımcı kavram soruları sorar ve projelerini konuşur: "let ile const farkı ne?", "Bu projede veritabanını neden böyle tasarladın?" Canlı kodlamada ise paylaşılan bir editörde ya da ekran paylaşarak küçük bir problem çözersin. Bu dersin büyük kısmı bu iki aşamaya hazırlık.
Ev ödevi
Bazı şirketler canlı kodlama yerine (ya da onunla birlikte) birkaç günlük süre verip küçük bir proje ister. Gerçek işe en çok benzeyen aşamadır: kodun yanında README, testler ve commit geçmişi de okunur. Ayrı bir bölümde ayrıntılı göreceksin.
Kültür ve ekip görüşmesi
Son aşamada genelde ekip lideri ya da yönetici, bazen de ekipten birkaç kişi olur. "Bir hatanı anlatır mısın?", "Bir konuda biriyle anlaşamadığın oldu mu?" gibi sorular gelir; bunlara nasıl cevap vereceğini proje anlatma bölümünde öğreneceksin.
Bu aşamada senin soru sorman da beklenir. Görüşmenin sonundaki "Sizin bir sorunuz var mı?" sorusuna "Yok" deme. Hazırda birkaç soru tut:
- "İlk üç ayda benden ne bekliyorsunuz? Başarılı bir junior o sürede ne yapmış olur?"
- "Kod incelemesi (code review) nasıl yapılıyor? Yeni gelenlere bir mentor veriliyor mu?"
- "Ekip hangi teknolojileri kullanıyor, bir özellik fikirden yayına nasıl gidiyor?"
- "Bu pozisyon neden açıldı?"
Sık sorulan JavaScript soruları
Teknik görüşmenin kavram soruları çoğunlukla aynı havuzdan gelir. Bu bölümde en sık gelen yedi soruyu geçeceksin. Her birinde önce çalışan bir örnek var, sonra mülakatta söyleyeceğin kısa cevap.
var, let, const ve hoisting
Soru: "var, let ve const arasındaki fark nedir?" Kursta hep let ve const kullandın. Ama internetteki eski kodlarda ve bazı iş yerlerinin eski projelerinde üçüncü bir anahtar sözcük göreceksin: var. Mülakatlarda bu yüzden sorulur. Önce nasıl davrandığına bak. degiskenler.js dosyasına yaz:
console.log(altin)var altin = 300console.log(altin)undefined300altin tanımlanmadan önce okundu ama hata çıkmadı, undefined yazdı. Ders 10'da hoisting'i görmüştün: Node dosyayı çalıştırmadan önce okur ve tanımları kayda geçirir. var ile açılan kutu bu okuma turunda hem kayda geçer hem de içine undefined konur. let ve const ile açılan kutu ise kendi satırı çalışana kadar kilitli kalır:
console.log(seviye)let seviye = 1C:\kod\mulakat\degiskenler.js:1console.log(seviye) ^ ReferenceError: Cannot access 'seviye' before initialization at Object.<anonymous> (C:\kod\mulakat\degiskenler.js:1:13) ... Node.js v24.20.0Kutunun kilitli olduğu bu aralığın adını Ders 10'da duymuştun: temporal dead zone. Mülakatçı soruyu bu adla da sorabilir; "kendi satırı çalışana kadar kilitli kalan aralık" diye açıklaman yeter.
İkinci fark kapsamda. let ve const blok kapsamlıdır: süslü parantezin içinde açılan kutu dışarıdan görünmez. var süslü parantezi dinlemez; yalnız fonksiyonun sınırına bakar.
if (true) { var mana = 50 let can = 100}console.log(mana)console.log(can)50C:\kod\mulakat\degiskenler.js:6console.log(can) ^ ReferenceError: can is not defined at Object.<anonymous> (C:\kod\mulakat\degiskenler.js:6:13) ... Node.js v24.20.0mana bloğun dışına sızdı; can sızmadı.
const hakkında da bir tuzak sorusu gelir: "const ile tanımlanan bir dizi değiştirilebilir mi?"
const takim = ["Ahri"]takim.push("Lux")console.log(takim)takim = ["Garen"][ 'Ahri', 'Lux' ]C:\kod\mulakat\degiskenler.js:4takim = ["Garen"] ^ TypeError: Assignment to constant variable. at Object.<anonymous> (C:\kod\mulakat\degiskenler.js:4:7) ... Node.js v24.20.0const kutunun içindekini dondurmaz; yalnız kutuya başka bir şey konmasını engeller. Dizinin içine eleman eklemek serbest, takim adına yeni bir dizi bağlamak yasak. Bunu Ders 4'te objeler için görmüştün.
var: bütün zamanlayıcılar aynı sayıyı yazıyorNe zaman çıkar? var ile kurulan bir for döngüsünün içinde, döngü sayacını sonradan çalışacak bir fonksiyonda (zamanlayıcı, olay dinleyicisi) kullanınca. Hata mesajı yok, sonuç yanlış. Mülakatlarda en sık sorulan "bu kod ne yazar?" sorusudur.
for (var i = 0; i < 3; i++) { setTimeout(() => console.log("var:", i), 0)}for (let j = 0; j < 3; j++) { setTimeout(() => console.log("let:", j), 0)}var: 3var: 3var: 3let: 0let: 1let: 2Neden? var i döngünün tamamı için tek bir kutudur. Zamanlayıcılar döngü bittikten sonra çalışır (Ders 15); o anda kutuda 3 vardır ve üç fonksiyon da aynı kutuya bakar. let j ise her turda yeni bir kutu açar; her fonksiyon kendi turunun kutusunu hatırlar.
Nasıl düzelir? var yerine let yaz. Yeni kodda var kullanmamanın en somut sebebi bu.
== ile ===
Soru: "== ile === arasındaki fark nedir?" Ders 2'de kuralı öğrendin: her zaman ===. Mülakatçı nedenini duymak ister. esitlik.js:
console.log(0 == "")console.log(0 == "0")console.log("" == "0")console.log(null == undefined)console.log(null === undefined)console.log(NaN === NaN)truetruefalsetruefalsefalseİlk üç satıra bak: 0 hem boş metne ("") hem "0" metnine eşit, ama bu iki metin birbirine eşit değil. == karşılaştırmadan önce iki tarafı aynı türe çevirmeye çalışır ve bu çevirmenin kuralları sezgiye uymaz. Gündelik mantıkta "A, B'ye eşitse ve A, C'ye eşitse B de C'ye eşittir"; == bu kuralı bile bozuyor.
Son iki satır da birer klasik. null == undefined sonucunun true olması, =='nin işe yarayan tek gevşekliği sayılır: bazı kod tabanlarında "null ya da undefined mı?" diye sormak için yalnız bu kalıpta == kullanılır. NaN ise kendine bile eşit değildir; bir değerin NaN olup olmadığını Ders 9'daki Number.isNaN ile sorarsın.
Closure
Soru: "Closure nedir, nerede kullanılır?" Ders 10'da sayaç ve bekleme süresi örnekleriyle gördün. Mülakat için yeni bir örnek: dışarıdan doğrudan değiştirilemeyen bir cüzdan. closure.js:
function cuzdanOlustur(baslangic) { let altin = baslangic return { harca: (miktar) => { if (miktar > altin) { return false } altin = altin - miktar return true }, bakiye: () => altin }} const cuzdan = cuzdanOlustur(300)console.log(cuzdan.harca(250))console.log(cuzdan.harca(100))console.log(cuzdan.bakiye())console.log(cuzdan.altin)truefalse50undefinedcuzdanOlustur çağrılıp bittiğinde altin kutusu ortadan kalkmadı; içerideki iki ok fonksiyonu onu hâlâ görüyor ve değiştiriyor. Dışarıdan cuzdan.altin diye ulaşmak ise mümkün değil: döndürülen objede böyle bir alan yok. Altını değiştirmenin tek yolu harca, o da paranın yetip yetmediğine bakıyor. Ders 16'daki özel alanların (#hp) sınıfsız hâli gibi.
Son cümle React'teki anlık görüntü davranışına bağlanıyor. Bu bağlantıyı kurabilmek, iki konuyu ayrı ayrı ezberlemekten çok daha güçlü bir izlenim bırakır.
Olay döngüsü
Soru: "Olay döngüsü nedir?" Genelde bir "bu kod hangi sırayla yazar?" sorusuyla gelir. olay-dongusu.js:
console.log("1: senkron")setTimeout(() => { console.log("2: setTimeout")}, 0)Promise.resolve().then(() => { console.log("3: then")})console.log("4: senkron")1: senkron4: senkron3: then2: setTimeout1 ve 4'ün önce gelmesini Ders 15'ten biliyorsun: kuyruktaki hiçbir iş, o anda çalışan kod bitmeden başlamaz. Yeni olan, then'in setTimeout'tan önce çalışması. İkisinin de beklediği bir şey yok; sebep, ayrı kuyruklarda beklemeleri.
then'i ve await'ten sonraki kod mikro görev kuyruğuna, zamanlayıcılar görev kuyruğuna girer. Yığın her boşaldığında önce mikro görevlerin hepsi, sonra görev kuyruğundan bir iş çalışır.await de bu kuyruğu kullanır. await'e gelen bir async fonksiyon orada durur, kalanı mikro görev olarak sıraya girer ve çağıran kod yoluna devam eder:
async function yukle() { console.log("B") await null console.log("D")} console.log("A")yukle()console.log("C")ABCDB, yukle çağrılır çağrılmaz yazıldı: async fonksiyonun ilk await'e kadarki kısmı hemen, senkron çalışır. await null beklenecek gerçek bir iş olmasa da fonksiyonu durdurdu; D ancak dosyanın kalanı (C) bitince geldi.
Promise ve async/await
Soru: "Promise nedir? async/await ne işe yarar?" İlk ek soru çoğu zaman "Bir async fonksiyon ne döndürür?" olur. async.js:
async function sampiyonGetir() { return "Ahri"} const sonuc = sampiyonGetir()console.log(sonuc)sonuc.then((isim) => { console.log(isim)})Promise { 'Ahri' }Ahrireturn "Ahri" yazsan da async fonksiyon her zaman bir Promise döndürür; değere await ya da then ile ulaşırsın. Ders 15'te Promise { <pending> } çıktısını görmüştün; buradaki Promise çoktan tamamlandığı için içindeki değer de görünüyor.
İkinci sık ek soru, hataların nasıl yakalandığı. Bir tuzakla birlikte gelir:
await olmadan try hatayı yakalamazNe zaman çıkar? try içinde bir async fonksiyonu çağırıp await yazmayı unutunca. Hata Promise'in içinde kalır, try bloğu çoktan bitmiş olur.
async function getir() { throw new Error("Sunucu yanıt vermedi")} async function main() { try { getir() } catch (hata) { console.log("Yakalandı:", hata.message) } console.log("main bitti")} main()main bittiC:\kod\mulakat\async.js:2 throw new Error("Sunucu yanıt vermedi") ^ Error: Sunucu yanıt vermedi at getir (C:\kod\mulakat\async.js:2:11) ... Node.js v24.20.0catch hiç çalışmadı: Yakalandı: satırı yok. main bitti yazıldı, program ondan sonra yakalanmamış ret yüzünden çöktü (Ders 15).
Nasıl düzelir? Çağrının önüne await yaz: await getir(). Fonksiyon Promise reddedilene kadar bekler ve ret, try'ın içinde bir hataya dönüşür.
async function getir() { throw new Error("Sunucu yanıt vermedi")} async function main() { try { await getir() } catch (hata) { console.log("Yakalandı:", hata.message) } console.log("main bitti")} main()Yakalandı: Sunucu yanıt vermedimain bittithis
Soru: "this neyi gösterir?" Ders 16'da sınıflarla öğrendin. Mülakatlarda genelde metodu objeden koparan bir örnekle sorulur. this.js:
this kayboluyor: Cannot read properties of undefined (reading 'isim')Ne zaman çıkar? Bir metodu objeden alıp ayrı bir değişkene koyduğunda ya da başka bir fonksiyona verdiğinde (callback). Çağrı artık obje.metot() biçiminde olmadığı için metodun içinde this boş kalır.
class Sampiyon { constructor(isim) { this.isim = isim } tanit() { console.log("Ben " + this.isim) }} const ahri = new Sampiyon("Ahri")ahri.tanit() const tanit = ahri.tanittanit()Ben AhriC:\kod\mulakat\this.js:7 console.log("Ben " + this.isim) ^ TypeError: Cannot read properties of undefined (reading 'isim') at tanit (C:\kod\mulakat\this.js:7:35) ... Node.js v24.20.0Nasıl düzelir? this'i Ders 16'daki gibi bind ile sabitle ya da metodu bir ok fonksiyonunun içinden, objesiyle birlikte çağır. İki yolu da kartın hemen altındaki kodda görüyorsun.
class Sampiyon { constructor(isim) { this.isim = isim } tanit() { console.log("Ben " + this.isim) }} const ahri = new Sampiyon("Ahri")const tanit = ahri.tanit.bind(ahri)tanit()setTimeout(() => ahri.tanit(), 0)Ben AhriBen Ahri12. satır this'i ahri'ye bağlanmış yeni bir fonksiyon üretiyor. 14. satırdaki ok fonksiyonu ise metodu yine ahri.tanit() biçiminde, objesiyle çağırıyor; zamanlayıcıya metodun kendisini değil, bu küçük sarmalayıcıyı veriyoruz.
| Çağrı biçimi | this neyi gösterir |
|---|---|
ahri.tanit() | noktanın solundaki obje: ahri |
const t = ahri.tanit; t() | undefined (sınıf içinde); metot objesinden koptu |
new Sampiyon("Ahri") | yapıcının içinde yeni oluşan obje |
| ok fonksiyonu | kendi this'i yok, yazıldığı yerinkini kullanır |
ahri.tanit.bind(ahri) | bind'a verilen obje, her çağrıda |
Değer ve referans
Soru: "JavaScript'te değerler kopyalanarak mı geçer, referansla mı?" Ders 12'de bütün ayrıntısıyla gördün. Mülakat sürümü referans.js:
let can = 100let yedekCan = canyedekCan = 50console.log(can) const ahri = { isim: "Ahri", hp: 100 }const ayniObje = ahriayniObje.hp = 50console.log(ahri.hp) const kopya = { ...ahri }kopya.hp = 80console.log(ahri.hp)console.log(ahri === ayniObje, ahri === kopya)console.log({ isim: "Lux" } === { isim: "Lux" })1005050true falsefalseSon satır mülakatların sevdiği bir sorudur: içerikleri aynı iki obje bile === ile eşit değil. === objelerde içeriğe değil kimliğe bakar: "aynı obje mi?" Fonksiyona argüman verirken de aynı kural geçerli:
function iyilestir(sampiyon, miktar) { sampiyon.hp = sampiyon.hp + miktar} function canArttir(hp, miktar) { hp = hp + miktar} const lux = { isim: "Lux", hp: 60 }let hp = 60iyilestir(lux, 20)canArttir(hp, 20)console.log(lux.hp, hp)80 60iyilestir dışarıdaki objeyi değiştirdi, canArttir dışarıdaki sayıyı değiştiremedi. Teknik olarak JavaScript her şeyi kopyalayarak verir; ama obje tutan bir değişkendeki "değer" objenin kendisi değil, referansıdır. Kopyalanan referans da aynı objeyi gösterir.
React soruları
React sorularında mülakatçı çoğunlukla üç şeyi yoklar: veri bileşenler arasında nasıl akıyor, liste nasıl çiziliyor, yan etkiler nerede yaşıyor. Aşağıdaki kod parçaları Ders 40–42'deki Vite projelerinde çalışacak biçimde yazıldı; burada çalıştırmıyoruz, cevabı desteklemek için okuyorsun.
State ve props
Soru: "State ile props arasındaki fark nedir?"
import { useState } from "react" function CanBari({ hp }: { hp: number }) { return <p>Can: {hp}</p>} export default function Sampiyon() { const [hp, setHp] = useState(100) return ( <div> <CanBari hp={hp} /> <button onClick={() => setHp(hp - 10)}>Vur</button> </div> )}hp, Sampiyon için state: bileşenin kendisi tutuyor ve setHp ile değiştiriyor. Aynı değer CanBari için props: dışarıdan geliyor, CanBari onu yalnız okuyor. Düğmeye basınca Sampiyon'un state'i değişir, React Sampiyon'u ve onunla birlikte CanBari'yi yeni değerle yeniden çizer.
key
Soru: "Listelerde key neden gerekli? Sıra numarasını key yapmak neden kötü?" Cevabın tamamı Ders 40'taki şekilde var:
Effect
Soru: "useEffect ne zaman çalışır? Bağımlılık dizisi ne işe yarar?"
import { useEffect, useState } from "react" export default function Sayac() { const [saniye, setSaniye] = useState(0) useEffect(() => { const zamanlayici = setInterval(() => { setSaniye((s) => s + 1) }, 1000) return () => clearInterval(zamanlayici) }, []) return <p>Maç süresi: {saniye} sn</p>}- Effect, bileşen ekrana çizildikten sonra çalışır. Çizim sırasında dış dünyaya dokunulmaz.
- Bağımlılık dizisi ne zaman yeniden çalışacağını söyler. Dizi hiç yoksa her çizimden sonra;
[]ise yalnız ilk çizimden sonra;[id]ise ilk çizimde veidher değiştiğinde. - Döndürülen fonksiyon temizlik fonksiyonudur. Effect yeniden çalışmadan önce ve bileşen sayfadan kaldırılınca çalışır. Burada zamanlayıcıyı durduruyor; yoksa bileşen gittikten sonra da sayaç dönmeye devam ederdi.
- Geliştirme sırasında katı mod (
<StrictMode>) her effect'i bir kez fazladan kurup temizler. Temizliği eksik bir effect'i böylece hemen fark edersin; yayındaki sitede bu olmaz. setSaniye((s) => s + 1)yazımı, güncelleme fonksiyonu kullanıyor.setSaniye(saniye + 1)yazsaydın effect yalnız ilk çizimde kurulduğu için hep ilk çiziminsaniye'sini, yani 0'ı görürdü; her saniye 0 + 1 hesaplanır, ekran 1'de takılı kalırdı. Bu da bir closure sorusu.
Kısa sorular
| Soru | Kısa cevap |
|---|---|
State'i neden doğrudan değiştirmiyoruz? (takim.push(...)) | React değişikliği yeni bir değer verildiğini görerek anlar. Aynı diziyi değiştirirsen fark etmez, ekran güncellenmez. Yeni dizi ver: setTakim([...takim, birim]) (Ders 41). |
setHp(hp - 10)'dan hemen sonra hp değişmiş olur mu? | Hayır. O çizimdeki hp bir anlık görüntüdür; yeni değer bir sonraki çizimde gelir. |
| Sunucu bileşeni ile istemci bileşeni farkı? (Next.js) | Sunucu bileşeni sunucuda çalışır, veritabanına doğrudan erişebilir, tarayıcıya JavaScript'i gitmez. State, olay ya da tarayıcı API'si gerekiyorsa dosyanın başına "use client" yazılır (Ders 43). |
SQL soruları
SQL soruları junior mülakatlarında genelde üç konuda toplanır: JOIN türleri, indeks ve tablo tasarımı. Örnekler için yeni iki tablo kur. Bu kez şampiyonlar yerine sana daha yakın bir konu var: iş başvuruların. Şirket adları uydurma.
CREATE TABLE sirketler ( id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, ad text NOT NULL, sehir text NOT NULL); CREATE TABLE basvurular ( id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sirket_id integer REFERENCES sirketler (id), pozisyon text NOT NULL, durum text NOT NULL); INSERT INTO sirketler (ad, sehir) VALUES ('Kuzey Yazılım', 'İstanbul'), ('Mavi Bulut', 'Ankara'), ('Piksel Oyun', 'İzmir'), ('Veri Atölyesi', 'İstanbul'); INSERT INTO basvurular (sirket_id, pozisyon, durum) VALUES (1, 'Junior Frontend', 'mülakat'), (1, 'Junior Full Stack', 'red'), (2, 'Stajyer Yazılımcı', 'bekliyor'), (3, 'Junior Oyun Geliştirici', 'teklif'), (NULL, 'Junior Backend (gizli ilan)', 'bekliyor');İki ayrıntıya dikkat et: Veri Atölyesi'ne henüz başvurmadın, son başvurunun ise şirketi belli değil. Bazı ilanlarda şirket adı gizlenir, bu yüzden sirket_id boş (NULL) bırakılabiliyor. JOIN türlerinin farkı tam da bu iki satırda ortaya çıkacak.
JOIN türleri
Soru: "INNER JOIN ile LEFT JOIN farkı nedir?" Önce INNER JOIN:
SELECT s.ad, b.pozisyonFROM sirketler AS sINNER JOIN basvurular AS b ON b.sirket_id = s.idORDER BY b.id;| ad | pozisyon |
|---|---|
| Kuzey Yazılım | Junior Frontend |
| Kuzey Yazılım | Junior Full Stack |
| Mavi Bulut | Stajyer Yazılımcı |
| Piksel Oyun | Junior Oyun Geliştirici |
Yalnız iki tarafta da eşi olan satırlar geldi: Veri Atölyesi de gizli ilan da yok. Şimdi LEFT JOIN:
SELECT s.ad, b.pozisyonFROM sirketler AS sLEFT JOIN basvurular AS b ON b.sirket_id = s.idORDER BY s.id, b.id;| ad | pozisyon |
|---|---|
| Kuzey Yazılım | Junior Frontend |
| Kuzey Yazılım | Junior Full Stack |
| Mavi Bulut | Stajyer Yazılımcı |
| Piksel Oyun | Junior Oyun Geliştirici |
| Veri Atölyesi | NULL |
Soldaki tablonun (sirketler) bütün satırları geldi; başvurusu olmayan Veri Atölyesi'nin sağ tarafı NULL ile dolduruldu. Ders 36'da bu ikisini kullandın, öteki ikisine yalnız bir bilgi kutusunda değindik. Mülakatta "başka hangi JOIN'ler var?" diye sorulabilir:
SELECT s.ad, b.pozisyonFROM sirketler AS sFULL JOIN basvurular AS b ON b.sirket_id = s.idORDER BY s.id, b.id;| ad | pozisyon |
|---|---|
| Kuzey Yazılım | Junior Frontend |
| Kuzey Yazılım | Junior Full Stack |
| Mavi Bulut | Stajyer Yazılımcı |
| Piksel Oyun | Junior Oyun Geliştirici |
| Veri Atölyesi | NULL |
| NULL | Junior Backend (gizli ilan) |
FULL JOIN (tam adıyla FULL OUTER JOIN) iki taraftaki eşsiz satırları da getirdi. RIGHT JOIN ise LEFT'in aynasıdır: sağdaki tablonun bütün satırları gelir. Aynı sonucu tabloların yerini değiştirip LEFT JOIN ile de alabildiğin için pratikte az kullanılır.
sirketler solda, basvurular sağda. Her JOIN türü hangi satırları getirir, kaç satır çıkar?WHERE ile HAVING
Soru: "WHERE ile HAVING farkı nedir?" Ders 34'ten: WHERE satırları gruplamadan önce eler, HAVING grupları gruplamadan sonra eler. "İki ya da daha fazla başvurduğun şirketler" bir grup koşuludur:
SELECT sirket_id, count(*) AS adetFROM basvurularGROUP BY sirket_idHAVING count(*) >= 2;| sirket_id | adet |
|---|---|
| 1 | 2 |
Aynı koşulu WHERE'e yazarsan Postgres izin vermez, çünkü WHERE çalışırken henüz grup ve sayı yoktur:
SELECT sirket_id, count(*) AS adetFROM basvurularWHERE count(*) >= 2GROUP BY sirket_id;ERROR: aggregate functions are not allowed in WHEREİndeks
Soru: "İndeks nedir? Her sütuna indeks açar mısın?" Başvuru tablosu büyüdükçe "bu şirkete yaptığım başvurular" sorgusu ve JOIN'ler sirket_id sütununda arama yapacak. Ders 36'daki gibi o sütuna indeks aç ve tablonun indekslerine bak:
CREATE INDEX basvurular_sirket_id_idx ON basvurular (sirket_id);SELECT indexnameFROM pg_indexesWHERE tablename = 'basvurular'ORDER BY indexname;| indexname |
|---|
| basvurular_pkey |
| basvurular_sirket_id_idx |
basvurular_pkey'i sen açmadın: Postgres her birincil anahtar için kendiliğinden bir indeks açar. Yabancı anahtar sütunları için ise açmaz; JOIN'lerde kullanılan sirket_id'ye indeksi senin açman gerekir.
Son cümledeki EXPLAIN'i ve çıktısındaki sorgu planını Ders 36'da denemiştin. Mülakatçı "indeksin kullanıldığını nereden anlarsın?" diye sorarsa cevap bu.
Normalizasyon
Soru: "Normalizasyon nedir?" En iyi cevap, yanlış bir tasarımı gösterip düzeltmektir. Şirket bilgisini her başvurunun içine yazsaydın:
CREATE TABLE basvurular_tek_tablo ( id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sirket_adi text NOT NULL, sirket_sehri text NOT NULL, pozisyon text NOT NULL, durum text NOT NULL);Kuzey Yazılım'a iki başvuru yaptın, şirketin adı ve şehri iki satırda yazılı. Şirket taşınırsa iki satırı da düzeltmen gerekir; birini unutursan veri kendi kendisiyle çelişir. Tek bir yazım hatası (Kuzey Yazilim) da aynı şirketi iki şirkete böler. Senin tasarımında şirket bilgisi tek yerde, başvurular ona sirket_id ile işaret ediyor.
Canlı kodlama: sesli düşünmek
Canlı kodlamada mülakatçı sana küçük bir problem verir ve çözüşünü izler. Çoğu zaman en önemli şey doğru cevap değil, oraya nasıl gittiğindir. Sessizce yazıp bitiren aday, dinleyene hiçbir şey göstermez; takıldığında da yardım alamaz. Düşündüğünü yüksek sesle söyleyen aday ise yanlış bir yola girdiğinde bile mülakatçıya akıl yürütmesini gösterir ve çoğu zaman bir ipucuyla geri döner.
Her problemde aynı beş adımı izle:
Şimdi bu adımları gerçek bir mülakat sorusuyla, baştan sona uygula.
1. adım: Soruyu netleştir
Mülakatçı: "Şampiyon adlarından oluşan bir dizi alıp en çok tekrar eden adı döndüren bir fonksiyon yazar mısın?"
Hemen yazmaya başlama. Önce soruyu kendi sözlerinle tekrarla ve belirsiz yerleri sor:
Sen: "Anladığım kadarıyla girdi bir metin dizisi, çıktı tek bir ad. Birkaç şey sorayım: Dizi boş gelebilir mi, o zaman ne dönmeli? İki ad eşit sayıdaysa hangisini seçeyim? 'ahri' ile 'Ahri' aynı mı sayılsın?"
Mülakatçı: "Boşsa null dönsün. Eşitlikte dizide önce geçen kazansın. Büyük-küçük harfi dert etme, adlar hep aynı biçimde yazılmış gelecek."
Bu üç soru yarım dakikanı aldı ama problemin üç tuzağını önceden ortaya çıkardı. Mülakatçı bu soruları bekler; hiç sormadan yazmaya başlayan aday çoğunlukla sınır durumlarında takılır.
2. adım: Örnekleri ve sınır durumlarını yaz
en-cok.js dosyasını aç ve kod yazmadan önce örnekleri yorum olarak yaz. Bunlar birazdan test listen olacak:
// ["Ahri", "Lux", "Ahri", "Jinx"] -> "Ahri"// ["Lux"] -> "Lux"// [] -> null// ["Jinx", "Garen", "Garen", "Jinx"] -> "Jinx" (eşitlik)Biri sıradan durum, üçü sınır durumu: tek eleman, boş dizi, eşitlik. Bunları yüksek sesle söylemek, "bu aday kodunu kırmayı da düşünüyor" demektir.
3. adım: Planı söyle
Sen: "Diziyi bir kez gezip her adın kaç kez geçtiğini bir objede sayacağım. Sonra objedeki çiftleri gezip sayısı en büyük olanı seçeceğim. Eşitlikte önce geçen kazansın diye yalnız kesin büyükse değiştireceğim; obje anahtarları eklendiği sırayla geldiği için ilk görülen önde olur."
Bu planı yorum olarak da yazabilirsin. Mülakatçı burada "neden obje, neden iki tur?" diye sorabilir; ya da "güzel, devam et" der. Kötü bir planı kod yazmadan önce duyması ikiniz için de zaman kazandırır.
4. ve 5. adım: Kodla, dene
İlk akla gelen yazım reduce ile olabilir. Yazarken anlat, bitince örnekleri tek tek çalıştır:
TypeError: Reduce of empty array with no initial value (sınır durumu denenince)Ne zaman çıkar? reduce'a başlangıç değeri vermeden onu boş bir dizide çağırınca. İlk iki örnek doğru çıktı; boş dizi denenince program çöktü.
function enCokTekrar(isimler) { const sayac = {} for (const isim of isimler) { sayac[isim] = (sayac[isim] ?? 0) + 1 } const enIyi = Object.entries(sayac).reduce((aday, cift) => { return cift[1] > aday[1] ? cift : aday }) return enIyi[0]} console.log(enCokTekrar(["Ahri", "Lux", "Ahri", "Jinx"]))console.log(enCokTekrar(["Lux"]))console.log(enCokTekrar([]))AhriLuxC:\kod\mulakat\en-cok.js:6 const enIyi = Object.entries(sayac).reduce((aday, cift) => { ^ TypeError: Reduce of empty array with no initial value at Array.reduce (<anonymous>) ... Node.js v24.20.0Nasıl düzelir? Boş dizi geldiğinde Object.entries(sayac) de boş olur ve başlangıç değeri verilmemiş reduce neyle başlayacağını bilemez (Ders 11). İki yol var: boş diziyi fonksiyonun başında ayrıca ele almak (if (isimler.length === 0) return null) ya da en büyüğü düz bir döngüyle, başlangıç değerleri vererek bulmak.
Bu hatayı mülakatta görmek kötü değil, tam tersi: sınır durumunu kendi denemenle yakaladın. Yüksek sesle söyle: "Boş dizide reduce başlangıç değeri istiyor. En büyüğü düz bir döngüyle, null ve 0'dan başlayarak bulayım; böylece boş dizide kendiliğinden null döner."
// ["Ahri", "Lux", "Ahri", "Jinx"] -> "Ahri"// ["Lux"] -> "Lux"// [] -> null// ["Jinx", "Garen", "Garen", "Jinx"] -> "Jinx" (eşitlik) function enCokTekrar(isimler) { const sayac = {} for (const isim of isimler) { sayac[isim] = (sayac[isim] ?? 0) + 1 } let enIyiIsim = null let enIyiSayi = 0 for (const [isim, sayi] of Object.entries(sayac)) { if (sayi > enIyiSayi) { enIyiIsim = isim enIyiSayi = sayi } } return enIyiIsim} console.log(enCokTekrar(["Ahri", "Lux", "Ahri", "Jinx"]))console.log(enCokTekrar(["Lux"]))console.log(enCokTekrar([]))console.log(enCokTekrar(["Jinx", "Garen", "Garen", "Jinx"]))PS C:\kod\mulakat> node en-cok.jsAhriLuxnullJinxDört örnek, dört doğru cevap. Örnek yorumları en üstte kaldı; çıktıyı onlarla tek tek karşılaştırıp yüksek sesle oku.
Son çıktıları yorumlardaki beklentilerle birlikte oku: "Ahri bekliyordum, Ahri geldi. Boş dizide null. Eşitlikte Jinx, çünkü önce o geçiyor." Mülakatçının "Peki ["Garen", "Jinx", "Jinx", "Garen"] olsa?" diye sorması büyük ihtimal; cevap Garen, çünkü bu kez o önce geçiyor.
"Daha hızlı yapılabilir mi?"
Çalışan bir çözümden sonra sık gelen bir soru. Aklına gelebilecek başka bir yol: her ad için bütün diziyi yeniden saymak. İkisinin kaç adım attığını ölç. adim.js:
const isimler = []for (let i = 0; i < 1000; i++) { isimler.push("Şampiyon" + (i % 50))} // Yol A: her isim için bütün diziyi yeniden saylet adimA = 0for (const aranan of isimler) { let kac = 0 for (const isim of isimler) { adimA++ if (isim === aranan) { kac++ } }} // Yol B: diziyi bir kez gez, sayaca yaz; sonra sayacı gezlet adimB = 0const sayac = {}for (const isim of isimler) { adimB++ sayac[isim] = (sayac[isim] ?? 0) + 1}adimB = adimB + Object.keys(sayac).length console.log("Yol A:", adimA, "adım")console.log("Yol B:", adimB, "adım")Yol A: 1000000 adımYol B: 1050 adımYol A'da kac, aranan adın kaç kez geçtiğini sayıyor; en büyüğünü seçme kısmını kısa kalsın diye yazmadık, adım sayısını değiştirmiyor. Bin elemanlı bir dizide iç içe döngü bir milyon adım attı, sayaç objesi bin elli. Dizi on katına çıksa iç içe döngünün adımı yüz katına çıkar, sayacınki yalnız on katına. Mülakatta bunu sözle söylemen yeter: "Benim çözümüm diziyi bir kez geziyor, iç içe döngü yok; dizi büyüdükçe süre de aynı oranda büyüyor."
Takıldığında
- Sessiz kalma. "Şu an şurada takıldım: boş diziyi nasıl ele alacağımı düşünüyorum" demek, yarım dakika sessizlikten iyidir. Mülakatçı çoğu zaman bir ipucu verir.
- İpucunu al ve kullan. İpucu almak eksi puan değildir; ipucunu duymazdan gelip kendi yanlış yolunda ısrar etmek eksi puandır.
- Önce basit ve doğru, sonra hızlı. "Önce iç içe döngüyle çalışan bir sürüm yazayım, sonra hızlandırırım" demek meşru bir plandır. Hiç çalışmayan zekice bir çözümden iyidir.
- Adı unuttuysan söyle. "Diziyi sıralayan metodun adı
sort'tu sanırım, emin değilim" demek normaldir. Hatırlamadığın bir metot için kendi küçük döngünü de yazabilirsin. - Hata mesajını yüksek sesle oku. Kursun ilk dersinden beri yaptığın şey: dosya, satır, tür, açıklama.
Projeni anlatmak
"Bize bir projeni anlatır mısın?" sorusu neredeyse her görüşmede gelir. Kötü cevap bir özellik listesidir: "React kullandım, Next.js kullandım, giriş yaptım, Vercel'e attım." Dinleyen kişi bundan senin ne düşündüğünü, neyle uğraştığını öğrenemez. İyi cevap bir hikâyedir: bir problem vardı, şöyle çözdün, yolda şu zorluk çıktı, ondan şunu öğrendin.
| STAR | Proje anlatırken | Uzunluk |
|---|---|---|
| Durum + Görev | Problem: Ne vardı, neden sorundu, senin işin neydi? | 1–2 cümle |
| Eylem | Çözüm: Ne yaptın, neden öyle yaptın? | 2–3 cümle |
| Eylem | Zorluk: Yolda ne ters gitti, nasıl buldun ve çözdün? | 2–3 cümle |
| Sonuç | Öğrendiğin: Neyi artık farklı yapıyorsun? Sayı varsa ver. | 1–2 cümle |
En değerli kısım genelde zorluktur. Her şey yolunda giden bir hikâyede mülakatçı senin takılınca ne yaptığını göremez. Kendi kurs projelerinden bir örnek, Ders 38'deki veritabanı taşıma:
Problem: Takım API'm verisini bir JSON dosyasında tutuyordu. Yayına aldığım servisin ücretsiz planında disk geçiciydi; sunucu her yeniden başladığında eklenen takımlar kayboluyordu.
Çözüm: Veriyi PostgreSQL'e taşıdım. Dört ilişkili tablo tasarladım, eski JSON'u tek bir transaction içinde taşıyan bir betik yazdım ve uç noktaları parametreli sorgulara çevirdim. API'yi kullanan sayfalar bozulmasın diye adresleri ve JSON biçimini aynen korudum.
Zorluk: Taşımadan sonra ilk yeni takımı eklerken
duplicate keyhatası aldım; Postgres yeni takıma 1 numarasını vermeye çalışıyordu. Eski kayıtları kendi numaralarıyla taşıdığım için kimlik sayacı hiç ilerlememişti.setvalile sayacı en büyük numaraya getirdim ve bu adımı taşıma betiğine ekledim.Öğrendiğim: Veri taşımak yalnız satırları kopyalamak değil; sayaç gibi görünmeyen durumun da taşınması gerekiyor. Artık her taşımadan sonra bir ekleme denemesi yapıyorum.
Yüksek sesle okuyunca bir dakika kadar sürer. Mülakatçının ek soruları buradan çıkar: "Neden PostgreSQL? Neden transaction? Betik ikinci kez çalışırsa ne olur?" Bunların cevapları da senin projende. Bu yüzden anlattığın her projenin kodunu mülakattan önceki gün bir kez aç ve göz gezdir.
Takım API'si yaptım. Expresskullandım, PostgreSQLkullandım, Neon'da veritabanıaçtım, Render'a yükledim.Sayfalama ve arama da var.Takımlar, sunucu her yenidenbaşladığında kayboluyordu.Veriyi PostgreSQL'e taşıdım.Taşımadan sonra kimlik sayacıyüzünden ekleme bozuldu;setval ile düzeltip betiğeekledim. Artık her taşımadansonra bir ekleme deniyorum.İki dürüstlük kuralı:
- "Biz" değil "ben". Ekip projesinde senin yaptığın kısmı ayır: "Ekip olarak bir uygulama yaptık; ben giriş ve yetki kısmını yazdım." Mülakatçı seni işe alacak, ekibini değil.
- Kurs projesi olduğunu saklama. "Bir kursta yaptığım projenin üzerine şunları ekledim" demek zayıflık değil. Kurs projeleri birçok adayda aynıdır; seni ayıran, kendi eklediğin özellikler ve verdiğin kararlardır. Görevlerdeki genişletmeleri yaptıysan onları anlat.
Davranışsal sorular
Kültür görüşmesinde "Bana … yaşadığın bir zamanı anlatır mısın?" kalıbında sorular gelir. Bunlar da STAR ile cevaplanır.
Sık gelenler ve kurs boyunca yaşadıklarından hangisinin işe yarayabileceği:
- "Bir hatanı anlat." Aranan: hatayı sahiplenmek ve ders çıkarmak. Hikâye fikri: sınır durumunu atladığın bir görev; boş listede
NaNya da çöken birreduce, nasıl fark edip düzelttiğin. - "Uzun süre takıldığın bir sorun oldu mu?" Aranan: sistemli hata ayıklama. Hikâye fikri: kimlik sayacı ya da kaybolan
this; tahmin et, ölç, daralt adımlarıyla nasıl bulduğun (Ders 7). - "Bilmediğin bir şeyi hızla öğrenmen gerekti mi?" Aranan: öğrenme yöntemin. Hikâye fikri: yapay zekâ bloğu; belgeyi okuyup önce sahte istemciyle denemek (Ders 50).
- "Biriyle anlaşamadığın oldu mu?" Aranan: saygılı itiraz, ortak çözüm. Hikâye fikri: bir pull request incelemesinde gelen yorum (Ders 52) ya da bir takım projesi; gerekçeni anlatıp ortada buluşmak.
Bunlar yalnız fikir; gerçekten yaşadığın olayı anlat. İş deneyimin yoksa okul, staj, bir topluluk ya da oyun takımından örnek vermek de olur. Uydurma hikâye ilk ek soruda dağılır.
"Bilmiyorum ama şöyle bulurum"
Her mülakatta bilmediğin bir soru gelecek. Bu kaçınılmaz; asıl soru o anda ne yaptığın. Yanlış iki yol var: uydurmak ya da "bilmiyorum" deyip susmak. Uydurulan cevap mülakatçının ilk ek sorusunda ortaya çıkar ve diğer doğru cevaplarına da gölge düşürür. Yalnız "bilmiyorum" ise sana fırsat vermez.
Doğru yol dört adımlıdır:
- Dürüstçe söyle: "Bu terimi daha önce duymadım" ya da "Bunu kullanmadım."
- Bildiğin en yakın şeye bağla: "Ama adından ya da benzer bir konudan şunu tahmin ediyorum…"
- Tahmini tahmin olarak işaretle: "Yanılıyor olabilirim."
- Nasıl bulacağını söyle: "Böyle bir durumda resmî belgelere bakar, küçük bir deneme yazarım."
Bir örnek. React sorularının arasına şu gelsin: "React'te reconciliation nedir?" Bu kursta terimin İngilizcesini hiç kullanmadık. Ama konuyu biliyorsun:
Reconciliation, React'instate'i sunucuylaeşitlemesidir. Veriyiveritabanına yazar.Terimi bu adla duymadım."Uzlaştırma" demek; React'ineski listeyle yeni listeyikey'e bakarakkarşılaştırmasıyla ilgiliolabilir. Yanılıyorolabilirim; react.dev'debakar, küçük bir listeyledenerim.İkinci cevap doğru yolda: React her yeniden çizimde yeni arayüz tarifini öncekiyle karşılaştırır ve sayfada yalnız değişen kısımları günceller. key'in işi tam da bu karşılaştırmada öğeleri eşlemektir.
Aynı kalıp araç sorularında da işe yarar. "Docker kullandın mı?" sorusuna cevap: "Kullanmadım. Projelerimi Render ve Vercel'e yayına aldım; ortam değişkenlerini barındırıcıda tanımladım, logları okudum. Docker'ın uygulamayı bağımlılıklarıyla birlikte paketleyip her yerde aynı ortamda çalıştırdığını biliyorum; öğrenme listemde ilk sırada." Bilmediğini söyledin, ama yanındaki tecrübeni ve merakını da gösterdin.
Ev ödevi
Ev ödevi, mülakat aşamaları arasında gerçek işe en çok benzeyenidir. Bir görev tarifi gelir, birkaç gün süre verilir, sonunda bir GitHub deposu ya da sıkıştırılmış bir klasör teslim edersin. Kodu okuyan kişi aynı zamanda senin README'ni, testlerini ve commit geçmişini de okur.
Değerlendiren kişinin baktıkları, kabaca önem sırasıyla:
- Çalışıyor mu? README'deki komutlarla, senin bilgisayarın olmadan, ilk denemede çalışmalı.
- İstenen yapılmış mı? Tarifteki her madde. Fazlası artı değil; eksik bir zorunlu madde büyük eksi.
- Okunur mu? Anlamlı adlar, küçük fonksiyonlar, tutarlı biçim.
- Test var mı? Sınır durumlarını da kapsayan birkaç anlamlı test.
- README ve kararlar. Nasıl çalıştırılır, neyi varsaydın, neyi bilerek yapmadın.
- Commit geçmişi. Adım adım ilerlediğin görülüyor mu?
Örnek: maç özeti modülü
Tarif şöyle olsun: "Bir oyuncunun maç listesini alıp kazanma oranını ve en çok oynadığı şampiyonu döndüren bir JavaScript modülü yaz. Testleri ve README'si olsun. Süre üç gün, beklenen emek 3–4 saat." Proje klasörü C:\kod\mac-ozeti, içinde package.json'da "type": "module" ve "test": "node --test" betiği (Ders 14, Ders 53).
export function kazanmaOrani(maclar) { if (maclar.length === 0) { return 0 } const galibiyet = maclar.filter((mac) => mac.sonuc === "G").length return Math.round((galibiyet / maclar.length) * 100)} export function enCokOynanan(maclar) { const sayac = {} for (const mac of maclar) { sayac[mac.sampiyon] = (sayac[mac.sampiyon] ?? 0) + 1 } let enIyiIsim = null let enIyiSayi = 0 for (const [isim, sayi] of Object.entries(sayac)) { if (sayi > enIyiSayi) { enIyiIsim = isim enIyiSayi = sayi } } return enIyiIsim}enCokOynanan, canlı kodlamada yazdığın fonksiyonun aynısı; yalnız adları objelerin sampiyon alanından sayıyor. Testler ayrı bir dosyada:
import { test } from "node:test"import assert from "node:assert/strict"import { kazanmaOrani, enCokOynanan } from "./ozet.js" const maclar = [ { sampiyon: "Ahri", sonuc: "G" }, { sampiyon: "Lux", sonuc: "M" }, { sampiyon: "Ahri", sonuc: "G" }, { sampiyon: "Jinx", sonuc: "G" }] test("kazanma oranı yüzde olarak yuvarlanır", () => { assert.equal(kazanmaOrani(maclar), 75)}) test("maç yoksa kazanma oranı 0", () => { assert.equal(kazanmaOrani([]), 0)}) test("en çok oynanan şampiyonu bulur", () => { assert.equal(enCokOynanan(maclar), "Ahri")}) test("eşitlikte listede önce geçen seçilir", () => { const esit = [ { sampiyon: "Jinx", sonuc: "M" }, { sampiyon: "Garen", sonuc: "G" }, { sampiyon: "Garen", sonuc: "M" }, { sampiyon: "Jinx", sonuc: "G" } ] assert.equal(enCokOynanan(esit), "Jinx")}) test("maç yoksa en çok oynanan null", () => { assert.equal(enCokOynanan([]), null)})Beş testten üçü sınır durumu: boş liste iki kez, eşitlik bir kez. Değerlendiren kişi testlerin adlarını okuyarak senin hangi durumları düşündüğünü görür; test adlarını cümle gibi yazmanın sebebi bu.
PS C:\kod\mac-ozeti> npm test > mac-ozeti@1.0.0 test> node --test ✔ kazanma oranı yüzde olarak yuvarlanır (0.6841ms)✔ maç yoksa kazanma oranı 0 (0.1042ms)✔ en çok oynanan şampiyonu bulur (0.6309ms)✔ eşitlikte listede önce geçen seçilir (0.1255ms)✔ maç yoksa en çok oynanan null (0.1192ms)ℹ tests 5ℹ suites 0ℹ pass 5ℹ fail 0ℹ cancelled 0ℹ skipped 0ℹ todo 0ℹ duration_ms 77.5566Süreler (ms) her çalıştırmada biraz farklı çıkar.
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: NaN !== 0Ne zaman çıkar? kazanmaOrani'nın ilk hâlinde boş liste kontrolü yoktu. Boş listede 0 / 0 hesaplanır, sonuç NaN çıkar (Ders 9). "Maç yoksa kazanma oranı 0" testi bunu yakaladı:
PS C:\kod\mac-ozeti> npm test > mac-ozeti@1.0.0 test> node --test ✔ kazanma oranı yüzde olarak yuvarlanır (0.9131ms)✖ maç yoksa kazanma oranı 0 (1.087ms)ℹ tests 2ℹ suites 0ℹ pass 1ℹ fail 1ℹ cancelled 0ℹ skipped 0ℹ todo 0ℹ duration_ms 114.6365 ✖ failing tests: test at ozet.test.js:16:1✖ maç yoksa kazanma oranı 0 (1.087ms) AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: NaN !== 0 at TestContext.<anonymous> (file:///C:/kod/mac-ozeti/ozet.test.js:17:12) ...Nasıl düzelir? NaN !== 0: fonksiyon NaN döndürdü, test 0 bekliyordu. Fonksiyonun başına boş liste kontrolünü ekle (yukarıdaki ozet.js'in 2–4. satırları). Bu tür bir düzeltme, commit geçmişinde kendi commit'ini hak eder.
README
README, değerlendiren kişinin açtığı ilk dosyadır. Ders 7'de öğrendiğin Markdown'la, şu bölümleri kapsayan kısa bir metin yeter:
# Maç özeti Bir oyuncunun maç geçmişinden kazanma oranını ve en çok oynadığı şampiyonu çıkaran küçük bir JavaScript modülü. Piksel Oyun'un junior geliştirici başvurusu için ev ödevi olarak yazıldı. ## Çalıştırma Node.js 24 gerekir, dış paket yok. ```shnpm test``` ## Kullanım ```jsimport { kazanmaOrani, enCokOynanan } from "./ozet.js" const maclar = [ { sampiyon: "Ahri", sonuc: "G" }, { sampiyon: "Lux", sonuc: "M" }]kazanmaOrani(maclar) // 50enCokOynanan(maclar) // "Ahri"``` ## Varsayımlar - `sonuc` alanı "G" (galibiyet) ya da "M" (mağlubiyet) olur.- Kazanma oranı yüzde olarak, en yakın tam sayıya yuvarlanır.- Maç yoksa oran 0, en çok oynanan şampiyon `null` döner.- Eşitlikte listede önce geçen şampiyon seçilir. ## Zamanım olsaydı - Girdiyi doğrulardım: `sonuc` alanında "G" ve "M" dışında bir değer gelirse açık bir hata fırlatırdım.- Şampiyon adlarını büyük-küçük harf farkı gözetmeden sayardım ("ahri" ile "Ahri" aynı sayılsın). ## Süre Yaklaşık 3 saat.Varsayımlar bölümü en çok atlanan ama en çok işe yarayan bölümdür. Tarifte boş liste için ne dönmesi gerektiği yazmıyordu; sen bir karar verdin ve bunu açıkça yazdın. Değerlendiren kişi kararına katılmasa bile düşündüğünü görür.
Commit geçmişi
Bütün işi tek bir "bitti" commit'inde teslim etme. Küçük ve anlamlı adımlarla ilerle; geçmiş, nasıl çalıştığını anlatsın. Mesajları Ders 52'deki Conventional Commits biçiminde yazdık:
PS C:\kod\mac-ozeti> git log --onelinea7126d0 (HEAD -> main) docs: kullanımı ve varsayımları yaz5e90c58 feat: en çok oynanan şampiyonu bul31e84c2 fix: maç yoksa kazanma oranını 0 döndür00a4bc0 feat: kazanma oranını hesaplab168d58 chore: proje iskeletini kurHer satır bir adım, her mesaj ne yapıldığını söylüyor; baştaki tür de neyin değiştiğini bir bakışta gösteriyor: yeni özellik (feat), hata düzeltme (fix), belge (docs), bakım işi (chore). Her feat commit'i kendi testleriyle birlikte girdi. Ortadaki fix satırı, yukarıdaki hata kartındaki düzeltmenin ta kendisi. Kayıt kimlikleri sende farklı olur. Aynı beş adımın bir de şu hâlini düşün:
f321b8b (HEAD -> main) sonee3f22a düzeltme8816ead asdf9f51177 çalışıyor galiba4f28feb ilkBu geçmişten hiçbir şey öğrenilmez: hangi commit neyi değiştirdi, test ne zaman eklendi, hata nerede düzeltildi? Değerlendiren kişi bunu "çalışırken düşünmüyor" diye okur. İyi commit mesajının kuralını Ders 7'de ve Ders 52'de gördün: ne yaptığını emir kipiyle söyleyen kısa bir cümle (ekle, düzelt, döndür), her commit'te tek iş. Şirketin ilanında ya da ödev tarifinde başka bir biçim isteniyorsa ona uy.
Teslimden önce
Son adım: projeyi temiz bir klasöre indirip yalnız README'deki komutlarla çalıştır. Senin bilgisayarında çalışan ama depoya girmemiş bir dosya, .gitignore'a takılmış bir ayar ya da elle kurduğun bir paket burada ortaya çıkar. GitHub'a gönderdikten sonra, kendi adresinle:
cd C:\kodgit clone https://github.com/kullanici-adin/mac-ozeti.git mac-ozeti-denemecd mac-ozeti-denemenpm installnpm testTestler geçiyorsa deneme klasörünü sil ve teslim et.
Mülakattan sonra
Görüşme bittiğinde iş bitmez:
- Aynı gün not al. Gelen soruları, bilemediklerini, sana söylenen sonraki adımı yaz. Bilemediklerinin cevabını o gün öğren.
- Kısa bir teşekkür e-postası gönderebilirsin. Zorunlu değil ama iyi bir izlenimi pekiştirir. Üç dört cümle yeter:
Konu: Görüşme için teşekkürler Merhaba Ece Hanım, Bugünkü görüşme için teşekkür ederim.Ekibin kod incelemesini nasıl yaptığınıanlatmanız özellikle ilgimi çekti.Konuştuğumuz canlı kodlama sorusundaeşitlik durumunu sonradan bir kez dahadüşündüm; çözümüm aynı kalıyor,testlerini de yazdım. Süreçteki birsonraki adımı bekliyorum. İyi çalışmalar,Deniz- Başvuru takibine işle. Ders 54'te kurduğun başvuru takibine tarihi, aşamayı ve bir sonraki adımı yaz.
- Ret gelirse kişisel algılama; junior ilanlarına çok sayıda kişi başvurur. Kısa ve kibar bir e-postayla geri bildirim isteyebilirsin. Her şirket vermez; veren olursa altın değerindedir.
- Teklif gelirse yazılı iste: maaş, yan haklar, çalışma biçimi (ofis, uzaktan, karma), başlangıç tarihi, deneme süresi. Düşünmek için birkaç gün istemek normaldir.
İşe girdikten sonra: ilk 90 gün
Teklifi kabul ettin, ilk gün geldi. Kimse senden ilk hafta özellik yazmanı beklemez. İlk aylar öğrenmek, güven kazanmak ve küçük ama gerçek katkılarla geçer.
- 1–30. gün: öğren ve kur. Projeyi kendi bilgisayarında çalıştır; kurulum belgesinde eksik bulduğun adımları düzelt. Küçük bir hatayı düzeltip ilk pull request'ini birleştir. Ekipteki herkesle tanış.
- 31–60. gün: katkı ver. Küçük bir özelliği baştan sona yap: veritabanı, API, arayüz, test. Başkalarının PR'larını oku, soru sor. Bir konuda "bunu ona sorabilirsiniz" denecek kadar derinleş.
- 61–90. gün: sahiplen. Orta boy bir işi planlayıp bitir. Yöneticinle ilk üç ayı değerlendir, bir sonraki üç ay için hedef koy.
İlk günden itibaren bir not dosyası tut: kurulumda takıldığın yerler, öğrendiğin komutlar, kime ne sorulacağı. Hem sana hem senden sonra gelen kişiye işe yarar; kurulum belgesini düzeltmek yeni gelen birinin yapabileceği en değerli ilk katkılardan biridir.
Soru sormak
Yeni başlayan biri iki hatadan birine düşer: ya her takıldığı anda sorar, ya da günlerce tek başına uğraşıp hiç sormaz. İkincisi daha zararlıdır. Kendine bir süre koy: bir sorunla 30–60 dakika uğraştıysan ve ilerlemiyorsan sor. Sorarken karşındakinin zamanını düşün; ne denediğini de yaz:
Merhaba Can, kayıt formunda takıldım.Amaç: yeni kullanıcı kaydolunca hoşgeldin e-postası gitsin.Denediğim: e-posta fonksiyonunu kayıtişleminin sonuna ekledim; kendibilgisayarımda çalışıyor.Sorun: test ortamında e-posta gitmiyor;loglarda e-posta sunucusu ayarının boşgeldiği görünüyor.Sorum: test ortamının ortam değişkenlerinerede tanımlı, yenisini ben mieklemeliyim?Amaç, denediğin, sorun, soru: bu dört parça soruyu cevaplamayı kolaylaştırır. Çoğu zaman mesajı yazarken cevabı kendin bulursun; bu da lastik ördek yönteminin yazılı hâli.
Kod incelemesinde gelen yorumları kişisel algılama. "Burada adı daha açık yaz" yorumu sana değil koda yapılmıştır. Anlamadığın yorumu sor, katılmadığına gerekçeyle cevap ver, ikisinde de teşekkür et.
Öğrenmeye devam: yol haritası
Kurs seni junior seviyesine hazırladı; işte öğrenme hızlanarak sürecek. İş yerindeki ihtiyaç zaten yön gösterir, ama işe girmeden önce ya da ilk aylarda sırayla şu dört konuya yönel:
- Docker. Uygulamayı bağımlılıklarıyla birlikte bir kutuya (konteyner) koyar; "benim bilgisayarımda çalışıyordu" sorununu bitirir. Çoğu şirket kullanır. İlk adım: resmî "Get started" rehberi (docs.docker.com/get-started); sonra takım API'ni bir konteynerde çalıştır.
- Bulut. Render ve Vercel, bulutun üzerine kurulu kolay katmanlar. Büyük şirketler AWS, Google Cloud ya da Azure'u doğrudan kullanır. İlk adım: bir sağlayıcı seç; sunucu, veritabanı, depolama ve yetki kavramlarını onun belgelerinden öğren. Harcama sınırı koymadan hiçbir şey açma.
- Test derinliği. Bu kursta birim testi yazdın. Sıradaki adım API'yi gerçek veritabanıyla deneyen entegrasyon testleri ve tarayıcıyı süren uçtan uca testler. İlk adım: Playwright (playwright.dev);
npm init playwright@latestile kur, ansiklopedinin arama kutusuna bir test yaz. - Sistem tasarımı temelleri. Kıdem arttıkça "milyon kullanıcıda bu nasıl çalışır?" sorusu gelir: önbellek, kuyruk, yük dengeleme, veritabanı kopyaları. İlk adım: system-design-primer deposu (İngilizce); kendi projelerinin darboğazını düşünerek oku.
Algoritma ağırlıklı mülakat yapan şirketlere (büyük teknoloji şirketleri genelde böyledir) başvuracaksan listeye veri yapıları ve algoritmalar başlığını da ekle: diziler, objeler (hash tablosu), yığın, kuyruk, ağaç, sıralama ve arama, zaman karmaşıklığı. Her gün bir küçük problem çözmek, haftada bir uzun çalışmaktan daha iyi sonuç verir.
Belgelerin ve araçların arayüzü değişebilir; yukarıdaki komutlar ve adresler bu ders yazılırken (Ekim 2026) doğrulandı. Bir adres taşınmışsa aracın adını arayıp resmî sitesine git.
Görevler
Her görevi C:\kod\mulakat klasöründe, görevde yazan dosyada yap; SQL görevini dersteki tablolarla Neon'un SQL Editor'ünde. Önce kendin dene; takılırsan ipuçlarını sırayla aç. Canlı kodlama görevlerinde beş adımı izle ve yüksek sesle söyle; evde tek başınayken bile.
Kendi projeni anlat
Kurs projelerinden birini seç: şampiyon ansiklopedisi, metin RPG, maç notları uygulaması ya da not asistanı. Takım API'sinin taşıma hikâyesini bu derste hazır okudun; kendi cümlelerinle çalışmak için başka bir proje seç. proje-hikayesi.md adlı bir dosya aç ve dört başlık altında yaz: Problem, Çözüm, Zorluk, Öğrendiğim. Sonra süre tutarak yüksek sesle oku. Metnin şu listeden geçmeli:
[ ] Yüksek sesle 60–120 saniye sürüyor.[ ] Problem bir sorunla başlıyor, teknoloji listesiyle değil.[ ] Zorlukta gerçek bir takılma var: belirtisi, mesajı ya da sayısıyla.[ ] Zorluğu nasıl bulup çözdüğün yazıyor.[ ] Öğrendiğim kısmı, bugün farklı yaptığın somut bir alışkanlık.[ ] Cümleler "ben" ile kurulu; kurstan gelenle senin eklediğin ayrı.İpucu 1
Zorluk bulamıyorsan projenin commit geçmişine bak (git log --oneline): "düzelt" ya da fix: geçen her commit, bir zamanlar takıldığın bir yer. Ders 54'te README'ye yazdığın "Zorluklar ve öğrendiklerim" bölümü de hazır bir başlangıç.
İpucu 2
"X'in önemli olduğunu öğrendim" gibi genel bir cümle mülakatçıya bir şey söylemez. Öğrendiğim kısmını "Artık her … yapıyorum" kalıbıyla yaz.
İpucu 3
Bitirince mülakatçının gözüyle kendine üç ek soru sor ve cevaplarını birer cümleyle metnin altına ekle: "Neden böyle yaptın?", "Başka bir yol var mıydı?", "Bir daha yapsan neyi değiştirirdin?"
Çözümü göster
Senin metnin kendi projenden ve kendi yaşadığından olmalı. Aşağıdaki, Ders 25'teki ansiklopediden bir örnek:
Problem: Maçtan önce bir şampiyona hızlıca bakmak istiyordum: adıyla aramak, role göre süzmek, sık oynadıklarımı favori olarak işaretlemek. Bunu telefonda da rahat kullanabileceğim tek bir sayfada istedim.
Çözüm: Data Dragon'dan 173 şampiyonu çeken bir sayfa yazdım. Kodu dört modüle böldüm: veriyi getiren, ekrana çizen, favorileri localStorage'da tutan ve hepsini yöneten. Siteyi GitHub Pages'te yayınladım.
Zorluk: Erişilebilirliği denerken sayfayı yalnız klavyeyle gezdim. Bir kartın favori yıldızına Space ile basınca odak kayboluyor, sonraki Tab listenin en başına, Aatrox'a gidiyordu. Hata mesajı yoktu, fareyle de görünmüyordu. Sebebi, her basışta bütün listeyi baştan çizmemdi; odaktaki düğme de silinip yeniden kuruluyordu. Yalnız değişen düğmeyi güncelleyen bir fonksiyon yazdım, odak yıldızın üstünde kaldı.
Öğrendiğim: Ekranda bir şeyi baştan çizdiğimde artık "odak neredeydi, şimdi nerede?" diye soruyorum. Her yeni özelliği bir kez yalnız klavyeyle deniyorum.
Yüksek sesle bir dakika kadar sürüyor. Listeyle karşılaştır: problem bir ihtiyaçla başlıyor; zorlukta belirti (odak Aatrox'a gidiyor), sebep ve çözüm var; öğrendiğim kısmı bugün yapılan iki somut alışkanlık. Ek sorulardan birinin cevabı da hazır olmalı: "Neden bütün listeyi baştan çiziyordun?" "En kısa yazım oydu ve fareyle denerken sorun görünmüyordu."
Seviye atlat, aslını bozma
seviye.js'e seviyeAtlat(sampiyon) fonksiyonunu yaz. Aldığı objeyi değiştirmeden, seviyesi bir fazla olan yeni bir obje döndürsün. Seviye en çok 18 olabilir: 18'deki bir şampiyon için seviyesi yine 18 olan yeni bir obje dönsün. Şu satırlar:
const garen = { isim: "Garen", seviye: 6 }const yeniGaren = seviyeAtlat(garen)console.log(garen)console.log(yeniGaren)console.log(garen === yeniGaren) const lux = { isim: "Lux", seviye: 18 }console.log(seviyeAtlat(lux))console.log(seviyeAtlat({ isim: "Jinx", seviye: 17 }))şunu yazmalı:
{ isim: 'Garen', seviye: 6 }{ isim: 'Garen', seviye: 7 }false{ isim: 'Lux', seviye: 18 }{ isim: 'Jinx', seviye: 18 }İpucu 1
sampiyon.seviye = sampiyon.seviye + 1 yazarsan dışarıdaki garen de değişir. Yeni bir obje kurman gerekiyor.
İpucu 2
Yayma ile kopyalayıp bir alanı ezebilirsin: { ...sampiyon, seviye: 7 } (Ders 12).
Çözümü göster
function seviyeAtlat(sampiyon) { if (sampiyon.seviye >= 18) { return { ...sampiyon } } return { ...sampiyon, seviye: sampiyon.seviye + 1 }} const garen = { isim: "Garen", seviye: 6 }const yeniGaren = seviyeAtlat(garen)console.log(garen)console.log(yeniGaren)console.log(garen === yeniGaren) const lux = { isim: "Lux", seviye: 18 }console.log(seviyeAtlat(lux))console.log(seviyeAtlat({ isim: "Jinx", seviye: 17 })){ isim: 'Garen', seviye: 6 }{ isim: 'Garen', seviye: 7 }false{ isim: 'Lux', seviye: 18 }{ isim: 'Jinx', seviye: 18 }Sınır denemesi üç değerde yapıldı: 6 (sıradan), 17 (sınırın hemen altı, 18'e çıkmalı), 18 (sınırın kendisi, olduğu gibi kalmalı). 18'de de yeni obje döndürmek tutarlılık için: fonksiyonu kullanan biri dönen objeyi değiştirdiğinde asıl obje hiçbir durumda etkilenmesin. React'te state'i tam olarak böyle güncelliyorsun.
Şarjlı yetenek
sarj.js'e sarjOlustur(adet) fonksiyonunu yaz. Teemo'nun mantarları gibi, belli sayıda şarjı olan bir yetenek kursun ve bir fonksiyon döndürsün. Döndürülen fonksiyon her çağrıldığında bir şarj harcayıp kalanı söylesin; şarj bitince "Şarj yok" döndürsün. Kalan şarj dışarıdan okunamasın ve değiştirilemesin. İki ayrı yeteneğin şarjları birbirini etkilememeli. Şu satırlar:
const mantar = sarjOlustur(2)console.log(mantar())console.log(mantar())console.log(mantar()) const bos = sarjOlustur(0)console.log(bos()) const ikinci = sarjOlustur(1)console.log(ikinci())console.log(mantar())şunu yazmalı:
Mantar kondu, kalan şarj: 1Mantar kondu, kalan şarj: 0Şarj yokŞarj yokMantar kondu, kalan şarj: 0Şarj yokİpucu 1
Bu bir closure sorusu. Kalan şarjı sarjOlustur'un içinde bir let kutusunda tut; döndürdüğün fonksiyon onu görebilir, dışarısı göremez.
İpucu 2
Döndürülen fonksiyonun ilk işi kalanın 0 olup olmadığına bakmak olsun. Önce bak, sonra azalt; tersini yaparsan sıfırda eksiye düşersin.
Çözümü göster
function sarjOlustur(adet) { let kalan = adet return () => { if (kalan === 0) { return "Şarj yok" } kalan = kalan - 1 return "Mantar kondu, kalan şarj: " + kalan }} const mantar = sarjOlustur(2)console.log(mantar())console.log(mantar())console.log(mantar()) const bos = sarjOlustur(0)console.log(bos()) const ikinci = sarjOlustur(1)console.log(ikinci())console.log(mantar())Mantar kondu, kalan şarj: 1Mantar kondu, kalan şarj: 0Şarj yokŞarj yokMantar kondu, kalan şarj: 0Şarj yokSınır denemeleri: 0 şarjla başlayan yetenek ilk çağrıda "Şarj yok" diyor; ikinci'nin çağrılması mantar'ın sayacını etkilemiyor, çünkü her sarjOlustur çağrısı kendi kalan kutusunu açıyor. Mülakatta sorulursa: "Her çağrı yeni bir kapanış oluşturuyor, şarjlar birbirinden bağımsız."
En uzun galibiyet serisi
Bir canlı kodlama sorusu: "Maç sonuçlarını "G" (galibiyet) ve "M" (mağlubiyet) olarak tutan bir dizi alıp art arda gelen en uzun galibiyet serisinin uzunluğunu döndüren bir fonksiyon yaz." seri.js'e yaz ve beş adımı uygula: soruyu netleştir (kendine sor: boş dizi? hiç galibiyet yoksa?), örnekleri yorum olarak yaz, planı söyle, kodla, dene. Şu satırlar:
console.log(enUzunSeri(["G", "G", "M", "G", "G", "G", "M"]))console.log(enUzunSeri(["G", "M", "G", "G"]))console.log(enUzunSeri([]))console.log(enUzunSeri(["M", "M"]))console.log(enUzunSeri(["G", "G", "G"]))şunu yazmalı:
32003İpucu 1
İki sayaç tut: şu anki serinin uzunluğu ve şimdiye kadar görülen en uzun seri.
İpucu 2
"G" gelince şu anki seriyi bir artır ve en uzunla karşılaştır. "M" gelince şu anki seriyi sıfırla.
İpucu 3
En uzunla karşılaştırmayı yalnız "M" gelince yaparsan, ["G", "G", "G"] gibi galibiyetle biten dizide son seri hiç sayılmaz. Bu yüzden son örnek orada.
Çözümü göster
// ["G", "G", "M", "G", "G", "G", "M"] -> 3// [] -> 0 (maç yok)// ["M", "M"] -> 0 (galibiyet yok)// ["G", "G", "G"] -> 3 (dizi galibiyetle bitiyor) // Plan: diziyi bir kez gez. G'de şu anki seriyi artır, en uzunu güncelle.// M'de şu anki seriyi sıfırla. function enUzunSeri(sonuclar) { let enUzun = 0 let simdiki = 0 for (const sonuc of sonuclar) { if (sonuc === "G") { simdiki++ if (simdiki > enUzun) { enUzun = simdiki } } else { simdiki = 0 } } return enUzun} console.log(enUzunSeri(["G", "G", "M", "G", "G", "G", "M"]))console.log(enUzunSeri(["G", "M", "G", "G"]))console.log(enUzunSeri([]))console.log(enUzunSeri(["M", "M"]))console.log(enUzunSeri(["G", "G", "G"]))32003Diziyi bir kez geziyor, iç içe döngü yok. Boş dizide döngü hiç dönmüyor ve başlangıç değeri 0 olduğu gibi dönüyor; ayrıca kontrol gerekmedi.
Hiç başvurulmayan şirket de görünsün
Bu dersteki sirketler ve basvurular tablolarıyla her şirketin adını ve o şirkete kaç başvuru yaptığını listeleyen bir sorgu yaz. Hiç başvurulmayan şirket de 0 ile görünmeli; şirketi gizli ilan listede olmamalı. Çok başvurulandan aza, eşitlikte ada göre sırala. Sonuç şu olmalı:
| ad | basvuru |
|---|---|
| Kuzey Yazılım | 2 |
| Mavi Bulut | 1 |
| Piksel Oyun | 1 |
| Veri Atölyesi | 0 |
İpucu 1
Başvurusu olmayan şirketin de gelmesi için hangi JOIN gerekiyor, hangi tablo solda olmalı?
İpucu 2
Her şirket için bir satır istiyorsun: GROUP BY s.id, s.ad (Ders 36). Sıralama için ORDER BY basvuru DESC, s.ad.
İpucu 3
count(*) satırları sayar. LEFT JOIN, Veri Atölyesi için de bir satır üretir (sağ tarafı NULL), bu yüzden count(*) ona 1 der:
SELECT s.ad, count(*) AS basvuruFROM sirketler AS sLEFT JOIN basvurular AS b ON b.sirket_id = s.idGROUP BY s.id, s.adORDER BY basvuru DESC, s.ad;| ad | basvuru |
|---|---|
| Kuzey Yazılım | 2 |
| Mavi Bulut | 1 |
| Piksel Oyun | 1 |
| Veri Atölyesi | 1 |
count(sütun) ise yalnız o sütunun NULL olmadığı satırları sayar. Hangi sütunu saymalısın?
Çözümü göster
SELECT s.ad, count(b.id) AS basvuruFROM sirketler AS sLEFT JOIN basvurular AS b ON b.sirket_id = s.idGROUP BY s.id, s.adORDER BY basvuru DESC, s.ad;| ad | basvuru |
|---|---|
| Kuzey Yazılım | 2 |
| Mavi Bulut | 1 |
| Piksel Oyun | 1 |
| Veri Atölyesi | 0 |
sirketler solda olduğu için her şirket en az bir satırla geldi; gizli ilanın şirketi olmadığı için hiç gelmedi. count(b.id) Veri Atölyesi'nin NULL olan başvuru kimliğini saymadı ve 0 verdi. Mülakatta bu soru çok sık gelir; tuzağı count(*)'tır.
Sınır denemesi: Veri Atölyesi'ne bir başvuru ekleyip (INSERT INTO basvurular (sirket_id, pozisyon, durum) VALUES (4, 'Veri Analisti', 'bekliyor');) sorguyu yeniden çalıştırırsan onun da 1'e çıktığını görürsün. Denedikten sonra o satırı sil (DELETE FROM basvurular WHERE pozisyon = 'Veri Analisti';) ki tablolar bu dersteki hâline dönsün.
Mülakatçının ek sorusu: ilk üç
Canlı kodlamada en çok tekrar eden şampiyonu buldun. Mülakatçı devam ediyor: "Şimdi en çok tekrar eden ilk üç şampiyonu, sayılarıyla birlikte döndür. Eşitlikte dizide önce geçen önde olsun. Üçten az farklı şampiyon varsa olanların hepsini, dizi boşsa boş dizi döndür." en-cok-uc.js'e yaz. Şu satırlar:
console.log(enCokUc(["Lux", "Ahri", "Jinx", "Ahri", "Garen", "Jinx", "Ahri", "Teemo", "Garen"]))console.log(enCokUc(["Lux", "Ahri", "Lux"]))console.log(enCokUc([]))console.log(enCokUc(["Teemo", "Annie", "Garen", "Yasuo"]))şunu yazmalı:
[ 'Ahri (3)', 'Jinx (2)', 'Garen (2)' ][ 'Lux (2)', 'Ahri (1)' ][][ 'Teemo (1)', 'Annie (1)', 'Garen (1)' ]İpucu 1
Sayma kısmı canlı kodlamadakiyle aynı. Değişen, en büyüğü seçmek yerine bütün çiftleri sayıya göre sıralamak.
İpucu 2
Object.entries(sayac) sana [isim, sayi] çiftlerinden bir dizi verir. Onu toSorted ve bir karşılaştırma fonksiyonuyla büyükten küçüğe sırala (Ders 11). JavaScript'in sıralaması eşit elemanların sırasını korur; eşitlikte önce geçenin önde kalması bu yüzden kendiliğinden olur.
İpucu 3
Sıralanmış diziden ilk üçü slice(0, 3) ile al, sonra map ile her çifti "Ahri (3)" biçimine çevir. slice dizide üçten az eleman varsa olanları verir; boş dizide boş dizi.
Çözümü göster
function enCokUc(isimler) { const sayac = {} for (const isim of isimler) { sayac[isim] = (sayac[isim] ?? 0) + 1 } return Object.entries(sayac) .toSorted((a, b) => b[1] - a[1]) .slice(0, 3) .map(([isim, sayi]) => isim + " (" + sayi + ")")} console.log(enCokUc(["Lux", "Ahri", "Jinx", "Ahri", "Garen", "Jinx", "Ahri", "Teemo", "Garen"]))console.log(enCokUc(["Lux", "Ahri", "Lux"]))console.log(enCokUc([]))console.log(enCokUc(["Teemo", "Annie", "Garen", "Yasuo"]))[ 'Ahri (3)', 'Jinx (2)', 'Garen (2)' ][ 'Lux (2)', 'Ahri (1)' ][][ 'Teemo (1)', 'Annie (1)', 'Garen (1)' ]Dört örnek dört ayrı durumu deniyor: sıradan durum ve eşitlik (Jinx ile Garen ikişer, Jinx önce geçiyor), üçten az farklı ad, boş dizi, hepsi eşit (ilk üçü sırayla). Mülakatçı "Sıralama ne kadar sürer?" diye sorarsa: "Farklı ad sayısı kadar elemanı sıralıyorum; dizi çok büyük ama farklı ad azsa bu ucuz. Sıralama tek turdan biraz daha pahalıdır."
Kendini sına
Soru 1
Bu program ne yazar?
console.log(puan)var puan = 10console.log(bonus)let bonus = 5Cevabı göster
Önce undefined, sonra hata: ReferenceError: Cannot access 'bonus' before initialization. var puan hoisting sırasında kayda geçer ve içine undefined konur; let bonus ise kendi satırı çalışana kadar kilitlidir. Program 3. satırda durur, bonus'a hiç 5 konmaz.
Soru 2
Mülakatçı şu kodu gösterip soruyor: "Bu program ne yazar, neden?"
const takim = ["Garen", "Lux", "Jinx"]for (var i = 0; i < takim.length; i++) { setTimeout(() => { console.log((i + 1) + ". sırada: " + takim[i]) }, 0)}Cevabı göster
Üç kez 4. sırada: undefined. var i döngünün tamamı için tek bir kutudur. Zamanlayıcılar döngü bittikten sonra çalışır; o anda kutuda 3 vardır: i + 1 4 yazar, takim[3] olmadığı için de undefined gelir. var'ı let yaparsan her tur kendi kutusunu açar ve çıktı 1. sırada: Garen, 2. sırada: Lux, 3. sırada: Jinx olur. Mülakatta bu cevabı iki üç cümleyle, kodu göstererek söyleyebilmelisin.
Soru 3
console.log("1" == 1, null == 0, null == undefined) ne yazar?
Cevabı göster
true false true. "1" == 1'de metin sayıya çevrilir, eşit olurlar. null == 0 beklenmedik biçimde false'tur: == null'ı yalnız undefined'a (ve kendine) eşit sayar, sayıya çevirmez. null == undefined ise true: == bu ikisini birbirine eşit sayar. Bu tutarsızlıklar === kullanmanın sebebi.
Soru 4
Bu program hangi sırayla yazar?
setTimeout(() => console.log("A"), 0)console.log("B")Promise.resolve().then(() => console.log("C"))console.log("D")Cevabı göster
B, D, C, A. Önce senkron kod çalışır (B, D). Yığın boşalınca olay döngüsü önce mikro görev kuyruğunu boşaltır (then ile C), sonra görev kuyruğundan bir iş alır (setTimeout ile A). setTimeout'un kodda en üstte olması sırayı değiştirmez.
Soru 5
Hangi satır hata verir, mesajı ne olur?
class Kule { constructor() { this.hasar = 150 } vur() { return this.hasar }} const kule = new Kule()const vur = kule.vurconsole.log(kule.vur())console.log(vur())Cevabı göster
13. satır 150 yazar, 14. satır hata verir: TypeError: Cannot read properties of undefined (reading 'hasar'). Hata 7. satırı (return this.hasar) gösterir. vur() objesiz çağrıldığı için metodun içinde this undefined'dır. Düzeltme: const vur = kule.vur.bind(kule) ya da () => kule.vur().
Soru 6
CanBari bileşeni, aldığı hp props'unu bir iksir düğmesiyle artırmak istiyor ve içinde hp = hp + 20 yazıyor. Ekran neden değişmiyor, doğrusu ne?
Cevabı göster
Props salt okunurdur; bileşen onu yalnız okur. hp = hp + 20 yalnız o fonksiyon çağrısındaki yerel kutuyu değiştirir, React bundan haberdar olmaz ve yeniden çizmez. Değer Sampiyon'un state'i olduğu için değiştirme işi de onda olmalı: Sampiyon, CanBari'ye props olarak bir fonksiyon verir (iksirIc={() => setHp(hp + 20)}), CanBari düğmeye basılınca o fonksiyonu çağırır. Veri yukarıdan aşağı props'la, değişiklik isteği aşağıdan yukarı fonksiyonla gider (Ders 41).
Soru 7
oyuncular tablosunda 10 oyuncu, maclar tablosunda 20 maç var. Her maçın bir oyuncusu var; 7 oyuncunun en az bir maçı var, 3'ünün hiç yok. oyuncular soldayken INNER JOIN ve LEFT JOIN kaç satır döndürür?
Cevabı göster
INNER JOIN 20 satır: her maç, oyuncusuyla eşleşen bir satır. LEFT JOIN 23 satır: aynı 20 eşleşme, artı maçı olmayan 3 oyuncu için birer satır (maç sütunları NULL). JOIN'in satır sayısını oyuncu sayısı değil, eşleşmeler belirler; bir oyuncunun 5 maçı varsa o oyuncu 5 satırda görünür.
Soru 8
Mülakatçı "Node.js'te process.nextTick ne işe yarar?" diye soruyor ve sen bunu hiç kullanmadın. Üç cevaptan hangisi en iyisi, neden? (a) "setTimeout'un daha hızlı hâlidir, hep onu kullanırım." (b) "Bilmiyorum." (c) "Kullanmadım. Adından bir sonraki adımda çalıştırılacak bir iş sıraya koyduğunu tahmin ediyorum; olay döngüsündeki kuyruklarla ilgili olabilir. Emin olmak için Node belgelerine bakar, küçük bir deneme yazarım."
Cevabı göster
(c). Dürüst, bildiği konuya (olay döngüsü, kuyruklar) bağlanıyor, tahmini tahmin olarak işaretliyor ve nasıl öğreneceğini söylüyor. Tahmin de doğru yolda: process.nextTick, Node'a özgü bir araçtır ve verilen fonksiyonu o anki iş biter bitmez, zamanlayıcılardan önce çalıştırır. Promise'lerin then'iyle sırası ise dosyanın CommonJS ya da ES modülü olmasına göre bile değişir; böyle ince bir ayrıntıyı ezberden söylemek yerine denemek gerçekten en doğru yol. (a) uydurma ve "hep onu kullanırım" ilk ek soruda çöker. (b) dürüst ama mülakatçıya hiçbir şey göstermiyor.
Son kontrol listesi
Bu ders bir commit ile değil, bu listeyle bitiyor. İlk başvurundan önce her maddeyi bir kez işaretle; mülakat sabahı yalnız son bölüme bak.
Başvurmadan önce
- GitHub profilinde en iyi üç proje sabitlenmiş; her birinin README'sinde ne yaptığı, nasıl çalıştırıldığı ve bir ekran görüntüsü ya da canlı bağlantısı var (Ders 54).
- Her proje için problem, çözüm, zorluk, öğrendiğin hikâyesi hazır ve en az bir kez yüksek sesle söylendi.
- 30–45 saniyelik kendini tanıtma metni ezbere değil, rahatça söylenebilir durumda.
- Bu dersteki yedi JavaScript sorusunun, üç React ve üç SQL sorusunun kısa cevaplarını notlarına bakmadan söyleyebiliyorsun.
- Görevlerdeki canlı kodlama sorularını bir kez de süre tutarak, yüksek sesle çözdün.
Mülakattan önceki gün
- Şirketin sitesine, ürününe ve ilan metnine yeniden baktın; ilandaki teknolojilerden hangilerini kullandığını, hangilerini kullanmadığını biliyorsun.
- Kültür görüşmesinde soracağın üç soru yazılı.
- Anlatacağın projelerin kodunu açıp göz gezdirdin.
- Çevrim içi görüşmeyse kamera, mikrofon ve ekran paylaşımı denendi; kod editörün ve terminalin büyük yazıyla açık.
Mülakat günü
- Su, kâğıt, kalem.
- Soruyu tekrar et, belirsizliği sor, düşündüğünü söyle.
- Bilmediğinde: bilmiyorum, bildiğim en yakın şey şu, şöyle bulurum.
- Görüşme biter bitmez: sorular ve bilemediklerin not edildi, başvuru takibine işlendi.
Yolun buraya kadarı
Kurs, "program ne demek?" sorusuyla başladı. Şimdi geriye dönüp bak:
İlk derste console.log("Hoş geldin") yazıp bir hata mesajını dört adımda okumayı öğrendin. Şimdi elinde bir veritabanına bağlanan, kullanıcı girişi olan, yayında çalışan ve bir dil modeliyle konuşan uygulamalar var. Bu, junior tam yığın ilanlarının çoğunda istenen yığının temeli. Her şeyi ezbere bildiğin anlamına gelmiyor; bilmediğin bir şeyi nasıl öğreneceğini bildiğin anlamına geliyor. Mülakatlarda da işte de asıl ölçülen bu.
Bu kursu baştan sona yaptıysan yüzlerce hata mesajı okudun, onlarca görevi kendi elinle yazdın ve takıldığın her yerden yürüyerek çıktın. Bu, tek başına çalışmayı ve takılınca vazgeçmemeyi gösteriyor; işe alımda tam olarak aranan şey. İlk başvurular ret de getirebilir. Her görüşmeden sonra listeni güncelle, bir sonrakine o listeyle gir.
Bu kursun son dersiydi; sonraki ders yok. Bundan sonrası senin projelerin, başvuruların ve ilk iş gününde açacağın ilk pull request.