એક બગ રિપોર્ટ આવ્યો જેણે દરેક ડિબગિંગ વૃત્તિને પડકારતી હતી. લો-એન્ડ એન્ડ્રોઇડ ફોન પરના યુઝર્સ કહેતા હતા કે એપ સીધી ગાયબ થઈ જતી હતી. લોન્ચ વખતે નહીં. કોઈ ચોક્કસ ટેપ કે સ્વાઇપ દરમિયાન પણ નહીં. સત્રના લગભગ વીસ મિનિટ પછી, સ્ક્રીન ફ્રીઝ થઈ જતી અને પ્રોસેસ બંધ થઈ જતી. લોગ્સ એકદમ સાફ હતા. QA તેમના હાઇ-એન્ડ હાર્ડવેર પર તેને ફરીથી રજૂ (reproduce) કરી શક્યા નહીં. તેને અનુસરવા માટે કોઈ સ્ટેપ્સ નહોતા. ત્રણ કલાકના મેમરી પ્રોફાઇલિંગ પછી, આખરે ચિત્ર સ્પષ્ટ થયું. એક સિંગલ ઇવેન્ટ લિસનર React hook ની અંદર બેઠું હતું. તે લિસનરે એક મોટા ડેટાસેટ પર ક્લોઝર (closure) બનાવ્યું હતું. કમ્પોનન્ટ અનમાઉન્ટ (unmount) થયો. લિસનર રહી ગયું. ડેટાસેટ મેમરીમાં રહી ગયો. 2GB RAM ધરાવતા ઉપકરણ પર, આ સંચયે હીપ (heap) ને ખાલી કરી દીધું અને ઓપરેટિંગ સિસ્ટમે એપને બંધ કરી દીધી. આ કોઈ સિન્ટેક્સ એરર કે લોજિકલ ખામી નહોતી. તે સ્કોપ (scope) બગ હતો, અને તે ઘાતક હતો.

ક્લોઝર લીક કેવી રીતે બને છે

મોટાભાગના ટ્યુટોરિયલ્સ સ્કોપને એક શૈક્ષણિક કોયડા તરીકે શીખવે છે કે વેરિએબલ ક્યાં દેખાય છે. પ્રોડક્શનમાં, સ્કોપ એ મેમરીના આયુષ્ય (lifetime) વિશેનો એક કરાર છે. જ્યારે JavaScript ફંક્શન કોઈ વેરિએબલ પર ક્લોઝર બનાવે છે, ત્યારે એન્જિન તે વેરિએબલને ત્યાં સુધી જીવંત રાખે છે જ્યાં સુધી તે ક્લોઝર પોતે પહોંચી શકાય તેમ હોય. React કમ્પોનન્ટમાં, આનો અર્થ એ છે કે યુઝર નેવિગેટ કરી જાય અને UI નોડ જતો રહે તે પછી પણ તમારો ડેટા લાંબા સમય સુધી ટકી રહે છે.

એક એવા hook નો વિચાર કરો જે window ઓબ્જેક્ટ પર લ

Temporal Dead Zone એ કોઈ સૈદ્ધાંતિક એજ કેસ નથી. જ્યારે તમે તેના ડિક્લેરેશનના અમલીકરણ પહેલા let અથવા const ને એક્સેસ કરો છો, ત્યારે એન્જિન ReferenceError ફેંકે છે. સર્ક્યુલર ડિપેન્ડન્સીઝ અને ડાયનેમિક ઈમ્પોર્ટ્સ ધરાવતા મોટા મોનોરેપોઝમાં, ચોક્કસ એક્ઝિક્યુશન ઓર્ડર ઘણીવાર ઈમ્પ્લીસિટ હોય છે. મોડ્યુલ A, મોડ્યુલ B ને ઈમ્પોર્ટ કરે છે, જે એવા ચંકને ડાયનેમિકલી ઈમ્પોર્ટ કરે છે જે ફરીથી મોડ્યુલ A પર નિર્ભર છે. જો કોઈ એક બ્રાન્ચ એવા વેરિએબલને સ્પર્શે જેનું ઈનિશિયલાઇઝેશન પૂરું થયું નથી, તો લોડિંગ દરમિયાન એપ ક્રેશ થઈ જાય છે. આ નિષ્ફળતાઓ હેરાન કરનારી છે કારણ કે તે સમય (timing) પર આધારિત છે. બંડલર સ્પ્લિટ પોઈન્ટ્સમાં નાનો ફેરફાર, કોડ-લોડિંગમાં નેટવર્ક વિલંબ, અથવા ચંક કેશિંગમાં ફેરફાર TDZ ને ટ્રિગર કરવા માટે પૂરતો ઓર્ડર બદલી શકે છે. ક્રેશ અનિશ્ચિત હોય છે, અને સ્ટેક ટ્રેસ સામાન્ય રીતે કોડની એકદમ નિર્દોષ લાઇન તરફ નિર્દેશ કરે છે.

રક્ષણાત્મક વ્યૂહરચનાઓ (Defensive Tactics)

સ્કોપ બગ્સથી બચવા માટે તમે સ્ટેક ટ્રેસ પર આધાર રાખી શકતા નથી. તમારે નિવારણ અને શોધ (prevention and detection) ની જરૂર છે.

સ્ટેટિક એનાલિસિસથી શરૂઆત કરો. કડક સીમાઓ લાગુ કરવા માટે ESLint કોન્ફિગર કરો. no-implicit-globals અને no-shadow જેવા નિયમો સ્પષ્ટ ભૂલો પકડી લે છે. શેડોઈંગ (Shadowing) ખાસ કરીને કપટી છે કારણ કે તે તમને એવું વિચારવા માટે છેતરે છે કે તમે લોકલ વેરિએબલને મ્યુટેટ કરી રહ્યા છો, જ્યારે તમે ખરેખર બહારના વેરિએબલ પર ક્લોઝર બનાવી રહ્યા હોવ છો અથવા અજાણતા ડુપ્લીકેટ બનાવી રહ્યા હોવ છો. આ નિયમો સ્પષ્ટ ઈરાદો (explicit intent) માટે મજબૂર કરે છે અને છૂપી અથડામણો (silent collisions) દૂર કરે છે.

તમારા યુનિટ ટેસ્ટમાં જે શિસ્તનો ઉપયોગ કરો છો તે જ શિસ્ત સાથે તમારી મેમરી પ્રોફાઇલ કરો. Chrome DevTools ખોલો, તમારા સ્ટાર્ટિંગ રૂટ પર હીપ સ્નેપશોટ લો, પાંચ મિનિટ માટે તમારી એપ્લિકેશનમાં નેવિગેટ કરો અને બીજો સ્નેપશોટ લો. આ બંનેની સરખામણી કરો. "Closure" માટે ફિલ્ટર કરો અને એવી સંખ્યાઓ શોધો જે સીમા વગર વધતી જાય છે. એવા ડિટેચ્ડ (detached) DOM નોડ્સ શોધો જે હજુ પણ ઇવેન્ટ લિસનર્સ ધરાવે છે. જો યુઝર કાઉન્ટ સ્થિર હોવા છતાં બીજા સ્નેપશોટમાં હજારો નવા Closure એન્ટ્રી દેખાય, તો તમે ડેટા પકડી રાખતા ફંક્શન્સ (trapped functions) બનાવ્યા છે. તે તમારો મેમરી લીક છે.

આર્કિટેક્ચરલ રીતે, કન્ફિગરેશન માટે ગ્લોબલ window ઓબ્જેક્ટનો ઉપયોગ કરવાનું બંધ કરો. સેટિંગ્સને props તરીકે અથવા ટાઈપ્ડ કોન્ટેક્સ્ટ (typed context) દ્વારા પાસ કરો. અહીં ડિપેન્ડન્સી ઇન્જેક્શન (Dependency injection) એ માત્ર એક એન્ટરપ્રાઇઝ બઝવર્ડ નથી; તે ફંક્શનને ગ્લોબલ સ્કોપમાં શોધવા દેવાને બદલે આર્ગ્યુમેન્ટ્સ દ્વારા તેની જરૂરિયાતની તમામ વસ્તુઓ આપવાની પદ્ધતિ છે. તેનું પરિણામ એવું કોડ છે જેને તમે બ્રાઉઝર શિમ્સ (browser shims) વગર ટેસ્ટ કરી શકો છો, અને એવા મોડ્યુલ્સ જે એક જ શેલમાં મલ્ટિપલ એપ્સ માઉન્ટ થાય ત્યારે અથડાતા નથી.

અંતે, ક્લીનઅપ ફેઝનું કડક રીતે પાલન કરો. દરેક addEventListener માટે ઇફેક્ટ ક્લીનઅપની અંદર મેચિંગ removeEventListener હોવું જરૂરી છે. અસિંક્રોનસ કામ માટે, AbortController નો ઉપયોગ કરો અને તેનો સિગ્નલ fetch ને પાસ કરો જેથી કમ્પોનન્ટ નાશ પામે ત્યારે ચાલુ રહેલી (in-flight) વિનંતીઓ કેન્સલ થઈ જાય. આ આદતો સ્કોપ કેટલો સમય જીવંત રહેશે તેના પર સીધો નિયંત્રણ રાખે છે. તે માત્ર બોઈલરપ્લેટ (boilerplate) નથી. તે મેમરી મેનેજમેન્ટ છે.

તમારી ટીમ માટે આનો અર્થ શું છે

સ્કોપ એ ઇન્ટરવ્યુ દરમિયાન ઉમેદવારોને પૂછવા માટેની કોઈ રમૂજી યુક્તિ નથી. પ્રોડક્શનમાં, સ્કોપ એ મેમરી મેનેજમેન્ટ છે. તમે જાહેર કરેલ દરેક વેરિએબલ એક સંભવિત બંધક છે. દરેક ક્લોઝર એ એન્જિન દ્વારા પાળવામાં આવનારું વચન છે. જ્યારે તમે લિસનરને રિલીઝ કરવાનું ભૂલી જાઓ છો, ત્યારે તમે માત્ર લાઈટ ચાલુ રાખી રહ્યા નથી. તમે તમારી એપ સાથે વજન બાંધી રહ્યા છો અને તેને સમુદ્રમાં ફેંકી રહ્યા છો. શક્તિશાળી હાર્ડવેર પર, એપ ગમે તેમ કરીને તરી જાય છે. લો-એન્ડ ડિવાઇસ વાપરતા યુઝર્સ માટે, તે ડૂબી જાય છે. સ્કોપને એક મર્યાદિત સંસાધન (finite resource) તરીકે ગણવાનું શરૂ કરો. તમારા યુઝર્સ અને તમારા ત્રણ કલાકના ડિબગિંગ સેશન્સ તમારો આભાર માનશે.