દરેક PDF લોડ ReferenceError સાથે ક્રેશ થઈ રહ્યો હતો. સ્ટેક ટ્રેસ (stack trace) કોઈ ઉપયોગી જગ્યા તરફ નિર્દેશ કરતો ન હતો, અને કોડનું માર્ગદર્શન આપતી સ્પષ્ટીકરણ (specification) કાગળ પર સંપૂર્ણપણે વ્યાજબી લાગતી હતી. તેમાં ફીચર ફ્લેગ તપાસવા અને દરેક રીજનની સરખામણી pageWidth ના અડધા સાથે કરવા માટે કહ્યું હતું. તેણે શું થવું જોઈએ તેનું વર્ણન કર્યું હતું. પરંતુ pageWidth ક્યાંથી આવવું જોઈએ તેનું વર્ણન કરવામાં તે નિષ્ફળ રહી હતી, અને તે એક જ ક્ષતિ આખા પાઇપલાઇનને તોડી પાડવા માટે પૂરતી હતી.

આર્કિટેક્ચર દસ્તાવેજો વર્તણૂક સમજાવવામાં સારા હોય છે. તેઓ ઘણીવાર સીમાઓ (boundaries) સમજાવવામાં ખૂબ જ નબળા હોય છે. "ફંક્શન pageWidth સામે x તપાસે છે" એવું કહેતું વાક્ય કોઈ ટેકનિકલ કરાર નથી; તે એક કથા છે જે સાદા અંગ્રેજીમાં એક નિર્ભરતા (dependency) છુપાવે છે. જ્યારે ડેવલપર તે વાક્ય વાંચે છે અને pageWidth ને નામ દ્વારા સંદર્ભિત કરતી મોડ્યુલ-લેવલ ફંક્શન લખે છે, ત્યારે કોડ સાચો લાગે છે કારણ કે તે વર્ણનને સંતોષે છે. પછી રનટાઇમ (runtime) તે નામ ઉકેલવાનો પ્રયાસ કરે છે, સ્કોપમાં કંઈપણ મળતું નથી, અને એરર ફેંકે છે.

એ રિફેક્ટર (Refactor) જે સરળ હોવું જોઈતું હતું

મેં પેજ એસેમ્બલી રિફેક્ટર દરમિયાન આ જ પેટર્ન જોઈ હતી. સ્પષ્ટીકરણમાં બે દેખીતી રીતે સ્પષ્ટ જરૂરિયાતો સૂચવવામાં આવી હતી:

  • FEATURE_LAYOUT તપાસો.
  • દરેક રીજનની સરખામણી pageWidth / 2 સાથે કરો.

ડેવલપરે સૂચનાઓનું વફાદારીપૂર્વક પાલન કર્યું. તેમણે મોડ્યુલ સ્કોપ પર એક યુટિલિટી ફંક્શન કાઢ્યું અને pageWidth ને પેરામીટર તરીકે દર્શાવ્યા વિના સીધું જ ફંક્શન બોડીમાં મૂકી દીધું. સ્પષ્ટીકરણમાં એવું જણાવ્યું ન હતું કે pageWidth આર્ગ્યુમેન્ટ લિસ્ટ (argument list) દ્વારા આવવું જોઈએ. તેણે એવું પણ જણાવ્યું ન હતું કે ફંક્શન મોડ્યુલ સ્કોપ પર છે, જ્યાં pageWidth હવે દેખાતું ન હતું. તેણે ફક્ત એવું માની લીધું કે અમલ કરનાર (implementer) એક્ઝિક્યુશન કોન્ટેક્સ્ટ (execution context) ને આંતરિક રીતે સમજી ગયો છે.

પરિણામ દરેક PDF લોડ પર ReferenceError હતું. કારણ કે વેરિએબલ મોડ્યુલ સ્કોપમાં નહોતો, ફંક્શને તરત જ એરર ફેંકી. જો સ્પષ્ટીકરણમાં ફંક્શનની સીમા અને તેના ઇનપુટ્સનું સ્પષ્ટપણે નામ આપ્યું હોત, તો ડેવલપર pageWidth ને પાસ કરત, અને આ બગ (bug) માળખાગત રીતે અશક્ય હોત. તેના બદલે, સૂચના એક જાળ (trap) જેવી હતી, જે અમલ કરનારને એવા પેરેન્ટ સ્કોપમાં પહોંચવા માટે લલચાવતી હતી જેનું અસ્તિત્વ જ નહોતું.

"Uses" ના ચાર અર્થો

ઊંડી સમસ્યા એ છે કે ગદ્યમાં (prose) ટાઇપ સિસ્ટમનો અભાવ છે. જ્યારે સ્પષ્ટીકરણ કહે છે કે "ફંક્શન X નો ઉપયોગ કરે છે," ત્યારે આધુનિક JavaScript કોડબેઝમાં આ વાક્ય ઓછામાં ઓછા ચાર ચોક્કસ રીતે અસ્પષ્ટ છે:

  • ફંક્શન X ને ફોર્મલ પેરામીટર તરીકે મેળવે છે.
  • ફંક્શન તે જ ફાઇલમાં જાહેર કરાયેલા મોડ્યુલ-લેવલ વેરિએબલમાંથી X વાંચે છે.
  • ફંક્શન નેસ્ટેડ પેરેન્ટ સ્કોપમાંથી X પર ક્લોઝર (close over) બનાવે છે.
  • ફંક્શન તેમાં પાસ કરવામાં આવેલા મોટા ઓબ્જેક્ટમાંથી X ને કાઢે છે.

આમાંથી દરેક વિકલ્પ સ્પષ્ટીકરણના શબ્દોને સંતોષે છે. તેમાંથી દરેક સ્ટેટિક એનાલિસિસ (static analysis) પાસ કરે છે. પરંતુ આપેલ સીમા માટે તેમાંથી માત્ર એક જ સાચો છે, અને ખોટી પસંદગી તે સીમા પર એવી રીતે ધારણાઓ લીક કરે છે જે સાયલન્ટલી (silently) કમ્પાઈલ થાય છે.

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

Web Workers પુરાવા ભૂંસી નાખે છે

એકવાર આર્કિટેક્ચરમાં Web Workers આવે પછી આ સમસ્યા ખરેખર ખતરનાક બની જાય છે. જ્યારે વર્કરની અંદર ભૂલ થાય છે, ત્યારે બ્રાઉઝર તમને સૌથી વધુ જરૂરી માહિતી દૂર કરી દે છે.

ખરેખર શું થાય છે તે અહીં છે. વર્કરની અંદર, એક અનકેચ એક્સેપ્શન (uncaught exception) ErrorEvent ફાયર કરે છે. જો વર્કર તે ભૂલને મેઇન થ્રેડ (main thread) પર ફોરવર્ડ કરે છે, તો સામાન્ય પદ્ધતિ message સ્ટ્રિંગ લેવાની અને તેને સીમા પાર મોકલવાની છે. મેઇન થ્રેડ તે સ્ટ્રિંગ મેળવે છે, તેમાંથી નવો Error ઓબ્જેક્ટ બનાવે છે, અને તેને લોગ કરે છે અથવા ફરીથી ફેંકે છે. DevTools માં જે દેખાય છે તે મેઇન થ્રેડના મેસેજ હેન્ડલરની અંદર પુનઃનિર્મિત ભૂલ છે. મૂળ ફાઇલનું નામ, લાઇન નંબર અને સ્ટેક ટ્રેસ (stack trace) કાઢી નાખવામાં આવે છે. નિષ્ફળતાનું વાસ્તવિક સ્થાન અદૃશ્ય થઈ જાય છે.

તેથી જ્યારે ખૂટતા pageWidth એ વર્કરની અંદર ReferenceError ટ્રિગર કરી, ત્યારે મેઇન થ્રેડે માત્ર "pageWidth is not defined" ટેક્સ્ટ રિપોર્ટ કર્યો જ્યાં મેસેજ હેન્ડલ કરવામાં આવ્યો હતો. વાસ્તવિક મોડ્યુલ-લેવલ ફંક્શન...