Steven
Steven7 λεπτά ανάγνωσης

Μία μεταβλητή CSS, πέντε γύροι ελέγχου κι ένα Swift toolchain που είπε ψέματα

Το GeekBye v2.0.7 έκανε ολόκληρο το overlay ρυθμιζόμενα ημιδιαφανές — που ακούγεται σαν μια αλλαγή CSS μιας γραμμής και σε καμία περίπτωση δεν ήταν. Η πραγματική ιστορία είναι τι έπιασε ο έλεγχος κώδικα: δύο επιφάνειες που αρνούνταν να ξεθωριάσουν, μια μπάρα εγγραφής που φαινόταν λάθος στην ίδια αδιαφάνεια, κι ένα μεταγλωττισμένο εκτελέσιμο που ταλαντεύτηκε κατά 672 bytes επειδή η δική μας τεκμηρίωση είπε σε έναν ελεγκτή τη λάθος έκδοση Swift.

Μηχανική
Σχεδιασμός
Build
Κυκλοφορίες GeekBye
Μία μεταβλητή CSS, πέντε γύροι ελέγχου κι ένα Swift toolchain που είπε ψέματα

Το «κάνε το overlay ημιδιαφανές» είναι το είδος εργασίας που ακούγεται σαν ένα slider κι μια ιδιότητα CSS opacity. Το GeekBye v2.0.7 παρέδωσε ακριβώς αυτό το αποτέλεσμα που βλέπει ο χρήστης — μία ρυθμιζόμενη «φωτεινή λειτουργία» που σε αφήνει να βλέπεις μέσα από τα panels του βοηθού ό,τι κι αν βρίσκεται πίσω τους. Αλλά το ενδιαφέρον μέρος αυτής της κυκλοφορίας δεν είναι το χαρακτηριστικό. Είναι οι πέντε γύροι ελέγχου που χρειάστηκαν για να βγει σωστό, κι μια πολεμική ιστορία για ένα μεταγλωττισμένο εκτελέσιμο που άλλαξε μέγεθος επειδή η δική μας τεκμηρίωση είπε ψέματα σε έναν ελεγκτή.

Ο σωστός τρόπος να κάνεις ένα ολόκληρο UI ημιδιαφανές

Η αφελής εκδοχή αυτού είναι να πασπαλίσεις μια τιμή αδιαφάνειας σε κάθε component panel και να το θεωρήσεις τελειωμένο. Αυτό είναι παγίδα συντήρησης: μια ντουζίνα hardcoded τιμές που αποκλίνουν τη στιγμή που κάποιος αγγίξει μία από αυτές.

Αντίθετα, το v2.0.7 χρησιμοποιεί ένα κοινό εργαλείο CSS κι ένα token ανά επιφάνεια. Υπάρχει μία κλάση overlay-surface που υπολογίζει το φόντο ενός panel από δύο custom properties: μια βασική αδιαφάνεια που κάθε επιφάνεια δηλώνει για τον εαυτό της (η μπάρα ελέγχου, το chat, οι φούσκες μεταγραφής, τα code blocks και ούτω καθεξής η καθεμία διαλέγει τη δική της), κι έναν καθολικό πολλαπλασιαστή άλφα που κάθεται στη ρίζα του overlay κι αναπαριστά τη ρύθμιση του slider του χρήστη. Κάθε επιφάνεια διαβάζει τον ίδιο πολλαπλασιαστή· η καθεμία κρατά τον δικό της χαρακτήρα. Κρίσιμα, κλιμακώνεται μόνο το φόντο του panel — το κείμενο, τα εικονίδια και το backdrop-blur παραμένουν πλήρως αποδοσμένα, οπότε «ημιδιαφανές» δεν σημαίνει ποτέ «μη αναγνώσιμο».

Δύο αποφάσεις σε αυτόν τον σχεδιασμό αξίζει να επισημανθούν, γιατί είναι η διαφορά ανάμεσα σε ένα χαρακτηριστικό που βοηθά κι σε ένα που παράγει tickets υποστήριξης:

  • Η προεπιλογή είναι πλήρως αδιαφανής. Η ρύθμιση αδιαφάνειας παραδίδεται στο 100% — ένα no-op. Το overlay κανενός δεν έγινε ξαφνικά διαφανές στην ενημέρωση· η ημιδιαφάνεια είναι εκεί όταν την αναζητήσεις κι αόρατη μέχρι τότε.
  • Η ρύθμιση επικυρώνεται αμυντικά. Υπάρχει μία πηγή αλήθειας για το min, το max, την προεπιλογή και το βήμα, συν ένας normalizer που φυλάει την κενή/απούσα περίπτωση. Αυτό έχει σημασία λόγω μιας παγίδας της JavaScript: το Number(null) είναι 0, οπότε μια απούσα ρύθμιση αφελώς μετατρεπόμενη θα καρφίτσωνε το overlay στην πιο διαφανή του κατάσταση αντί για την προεπιλογή του. Ο normalizer το πιάνει προτού μπορέσει να συμβεί.

Τράβα το slider κι ολόκληρο το overlay ξαναχρωματίζεται ζωντανά, εν μέσω εγγραφής, επειδή κάθε επιφάνεια είναι συνδρομητής στην ίδια ρύθμιση. Χωρίς επανεκκίνηση, χωρίς reload.

Τι έπιασε ο έλεγχος (κι ένα τεστ σε μία οθόνη δεν θα έπιανε ποτέ)

Εδώ η «μία μεταβλητή CSS» έπαψε να είναι απλή. Τρία bugs αναδύθηκαν στον έλεγχο, και δύο από αυτά είναι το ίδιο μάθημα φορώντας διαφορετικά καπέλα.

Ένα panel που δεν ήθελε να ξεθωριάσει. Το overlay των συντομεύσεων πληκτρολογίου έμεινε πεισματικά συμπαγές ενώ όλα τα υπόλοιπα γίνονταν ημιδιαφανή. Η αιτία: αποδίδεται εκτός του υποδέντρου DOM που κουβαλά τη μεταβλητή αδιαφάνειας, οπότε δεν κληρονόμησε ποτέ την τιμή που κατέβαινε σε cascade από τη ρίζα του overlay. Οι custom properties του CSS κληρονομούνται μέσα από το δέντρο DOM — κι αν ένας κόμβος δεν είναι στην πραγματικότητα απόγονος του σημείου όπου ορίζεις τη μεταβλητή, παίρνει σιωπηλά τίποτα. Η διόρθωση ήταν να ορίσουμε τη μεταβλητή τοπικά και σε εκείνο το panel.

Μια απόχρωση hover που εξαφανίστηκε. Τα κουμπιά Fn/Assist έχασαν την κυανή λάμψη hover τους. Το εργαλείο ημιδιαφάνειας όριζε το φόντο με !important (πρέπει να νικά τις προεπιλογές της επιφάνειας), κι αυτό ισοπέδωσε την hover: απόχρωση φόντου στο ίδιο στοιχείο. Σύγκρουση specificity. Η διόρθωση ήταν να κάνουμε την απόχρωση hover εξίσου εμφατική ώστε να μπορεί να ξανακερδίσει στο hover.

Και τα δύο είναι η ίδια υποκείμενη αλήθεια: μια cascade μεταβλητής CSS είναι πραγματικό όριο ορθότητας, όχι λεπτομέρεια styling. Η κληρονομικότητα κι το specificity αποφασίζουν αν η τιμή σου φτάνει καν στο στοιχείο, και κανένα από τα δύο δεν εμφανίζεται σε ένα screenshot μέχρι να είναι λάθος.

Ίδια αδιαφάνεια, διαφορετική εμφάνιση. Το πιο λεπτό: η μπάρα εγγραφής κι η μπάρα σε αδράνεια ήταν ρυθμισμένες στην ίδια ακριβώς βασική αδιαφάνεια, κι όμως φαίνονταν ορατά διαφορετικές. Γιατί; Το χάπι εγγραφής χρησιμοποιούσε βαρύτερο backdrop-blur, κι ένα ισχυρό blur πάνω σε σκούρο φόντο τείνει προς σκουρότερο αποτέλεσμα — έτσι ίση άλφα παρήγαγε άνιση εμφάνιση. Είχε επίσης ένα αχνό περίγραμμα εκεί όπου η μπάρα σε αδράνεια είχε μια φωτεινή ακμή δακτυλίου gradient. Η διόρθωση ταίριαξε το επίπεδο blur κι τον δακτύλιο. Το μάθημα έμεινε: ίση άλφα δεν είναι ίση εμφάνιση μόλις μπει στο κάδρο το blur. Η ημιδιαφάνεια είναι σύστημα αλληλεπιδρώντων εφέ, όχι ένας μόνο αριθμός.

Το εκτελέσιμο που άλλαξε μέγεθος επειδή η τεκμηρίωση είπε ψέματα

Τώρα η πολεμική ιστορία, κι είναι το αγαπημένο μου είδος — αυτή όπου τα εργαλεία κι η τεκμηρίωση διαφωνούν κι ένας προσεκτικός ελεγκτής πιάνεται στα διασταυρούμενα πυρά.

Το GeekBye παραδίδει κάποια προμεταγλωττισμένα εκτελέσιμα Swift (native βοηθοί για πράγματα όπως ανίχνευση συσκέψεων). Επειδή ένα μεταγλωττισμένο εκτελέσιμο είναι commited στο repo, κάθε προγραμματιστής πρέπει να το μεταγλωττίσει με ακριβώς την ίδια έκδοση Swift — διαφορετικά toolchains παράγουν διαφορετικά bytes από πανομοιότυπο πηγαίο κώδικα, κι αυτό θα σήμαινε ένα φάντασμα σύγκρουσης git κάθε φορά που κάποιος ξαναχτίζει. Έτσι υπάρχει ένα script που επιβάλλει μία συγκεκριμένη έκδοση: Swift 6.3.x, ελεγχόμενη κατά το build.

Κατά τον έλεγχο αυτής της κυκλοφορίας, ένας ελεγκτής επισήμανε ότι ένα εκτελέσιμο θα έπρεπε να ξαναχτιστεί ώστε να ταιριάζει με «το επιβεβλημένο από το repo toolchain». Ο προγραμματιστής έκανε ακριβώς αυτό — και το μέγεθος του εκτελέσιμου άλλαξε από 89,184 bytes σε 88,512. Το μήνυμα του commit μάλιστα έλεγε ότι «αποκαθιστούσε byte-stable έξοδο» ξαναχτίζοντας με Swift 6.2.1, «το επιβεβλημένο από το repo toolchain».

Μόνο που η 6.2.1 ήταν η λάθος έκδοση. Το script επιβολής στο build απαιτούσε 6.3.x. Το πρόβλημα ήταν ότι η πεζή τεκμηρίωση — το README κι ο οδηγός συνεισφερόντων — έλεγε ακόμα 6.2.1 σε μερικά σημεία. Έτσι ένας ευσυνείδητος ελεγκτής κι ένας ευσυνείδητος προγραμματιστής, κι οι δύο προσπαθώντας να κάνουν το σωστό, ακολούθησαν την τεκμηρίωση κατευθείαν στο να κάνουν commit ακριβώς το artifact που το script επιβολής υπάρχει για να απορρίψει. Ένα εκτελέσιμο 6.2.1 σε ένα repo 6.3.x είναι ακριβώς η απόκλιση που θα έσπαγε το καθαρό ξαναχτίσιμο κάθε άλλου προγραμματιστή.

Το επόμενο commit το διόρθωσε — κι η διόρθωση είναι το ζητούμενο. Δεν ξαναέχτισε απλώς το εκτελέσιμο πίσω στη μορφή του 6.3.x (μέγεθος αποκαταστημένο στα 89,184, ακριβώς εκεί όπου ξεκίνησε). Επίσης διόρθωσε τις ξεπερασμένες αναφορές στο «6.2.1» στην τεκμηρίωση ώστε η παγίδα να μην μπορεί να στηθεί ξανά. Το εκτελέσιμο ταλαντεύτηκε κατά 672 bytes και πίσω σε δύο commits, κι το πραγματικό bug δεν ήταν ποτέ το εκτελέσιμο. Ήταν ότι δύο πηγές αλήθειας — το script επιβολής κι η γραπτή τεκμηρίωση — διαφωνούσαν, κι ένας άνθρωπος εμπιστεύτηκε τη λάθος.

Τρία πράγματα που μας δίδαξε αυτή η κυκλοφορία

  1. Μια cascade μεταβλητής CSS είναι όριο ορθότητας. Δύο από τα τρία bugs του ελέγχου κατέληξαν σε μια μεταβλητή που δεν κληρονομήθηκε κι σε ένα !important που ισοπέδωσε μια κατάσταση hover. Αν μια τιμή πρέπει να φτάσει σε ένα στοιχείο μέσω κληρονομικότητας ή να νικήσει μέσω specificity, αυτό το μονοπάτι είναι λογική — επαλήθευσέ το σαν λογική, όχι σαν styling.
  2. Ίσες παράμετροι μπορούν να παράγουν άνισα αποτελέσματα. Η μπάρα εγγραφής κι η μπάρα σε αδράνεια μοιράζονταν μια αδιαφάνεια κι όμως φαίνονταν διαφορετικές, επειδή ένα διαφορετικό blur άλλαξε το αποτέλεσμα. Όταν ενοποιείς ένα οπτικό σύστημα σε έναν αριθμό, έλεγξε ότι οι άλλες μεταβλητές γύρω από κάθε επιφάνεια δεν αλλάζουν σιωπηλά το τι σημαίνει αυτός ο αριθμός.
  3. Όταν η τεκμηρίωση κι η επιβολή διαφωνούν, διόρθωσε τη διαφωνία — όχι μόνο το σύμπτωμα. Το bug δεν ήταν ένα κακό εκτελέσιμο· ήταν μια τεκμηρίωση που ονόμαζε το λάθος toolchain κι ένα script επιβολής που έλεγε αλλιώς. Το ξαναχτίσιμο του εκτελέσιμου αντιμετωπίζει το σύμπτωμα. Το να κάνεις κάθε πηγή αλήθειας να συμφωνεί είναι η πραγματική διόρθωση, γιατί κι ο επόμενος ελεγκτής θα εμπιστευτεί την τεκμηρίωση.

Το GeekBye v2.0.7 παρέδωσε την ομοιόμορφη ημιδιαφάνεια, με όλες τις οριακές περιπτώσεις ελεγμένες κι την τεκμηρίωση toolchain τακτοποιημένη. Είναι η τελευταία από τις κυκλοφορίες «ήσυχης τέχνης» πριν το sprint αξιοπιστίας που ακολούθησε. Για το θεμέλιο από κάτω της, δες τι απαιτεί πραγματικά μια έκδοση 2 (v2.0.0)· για την αδελφή κυκλοφορία εκλέπτυνσης, ήρεμο λογισμικό: η διόρθωση του τρεμοπαίγματος κι το chip λειτουργίας απάντησης (v2.0.3 + v2.0.5)· κι για το πώς το overlay μένει κρυμμένο σε μια κλήση, πώς να μένεις αόρατος κατά τον διαμοιρασμό οθόνης.

Σχετικά Άρθρα

Ήρεμο Λογισμικό: Σκοτώνοντας έναν Διακόπτη που Τρεμόπαιζε και Μαθαίνοντας το Chat να Λέει σε Ποια Λειτουργία Βρίσκεται
Steven
Steven6 λεπτά ανάγνωσης

Ήρεμο Λογισμικό: Σκοτώνοντας έναν Διακόπτη που Τρεμόπαιζε και Μαθαίνοντας το Chat να Λέει σε Ποια Λειτουργία Βρίσκεται

Δύο μικρές κυκλοφορίες του GeekBye, χωρίς εντυπωσιακές λειτουργίες — απλώς ένας διακόπτης ρυθμίσεων που σταμάτησε να τρεμοπαίζει και ένας βοηθός chat που επιτέλους σου λέει αν απάντησε σε λειτουργία σύσκεψης ή σε λειτουργία προγραμματισμού. Να τι πραγματικά κοστίζει το «ήρεμο λογισμικό», μια λεπτομέρεια τη φορά.

Προϊόν
Σχεδιασμός
Μηχανική
Η ανατομία της τέλειας κυκλοφορίας λογισμικού: Πώς ο έλεγχος κώδικα έπιασε αυτό που τα τεστ δεν μπορούσαν
Steven
Steven7 λεπτά ανάγνωσης

Η ανατομία της τέλειας κυκλοφορίας λογισμικού: Πώς ο έλεγχος κώδικα έπιασε αυτό που τα τεστ δεν μπορούσαν

Σε όλη τη σειρά του GeekBye v2, το ίδιο πράγμα συμβαίνει ξανά και ξανά: μια διόρθωση περνάει κάθε τεστ στο μηχάνημα του προγραμματιστή, και μετά ο έλεγχος κώδικα αποδεικνύει ότι θα είχε αποτύχει για σχεδόν όλους. Αυτή είναι η ροή εργασίας πίσω από εννέα κυκλοφορίες — η πύλη ελέγχου, οι διορθώσεις που πιάστηκαν από νωρίς και η πειθαρχία του «τεστ πριν την αποστολή» που μετατρέπει το «δουλεύει σ' εμένα» σε «δουλεύει».

Μηχανική
Διαδικασία
Έλεγχος Κώδικα
Τι Πραγματικά Χρειάζεται μια Έκδοση 2: 206 Commits από Ειλικρινείς Καταστάσεις
Steven
Steven7 λεπτά ανάγνωσης

Τι Πραγματικά Χρειάζεται μια Έκδοση 2: 206 Commits από Ειλικρινείς Καταστάσεις

Το GeekBye v2 δεν ήταν μια κυκλοφορία με νέες λειτουργίες. Ήταν 206 commits στραμμένα σε μία ιδέα: η εφαρμογή δεν πρέπει ποτέ να λέει ψέματα για την ίδια της την κατάσταση. Να τι κοστίζει αυτό — μαζί με το λάθος μιας γραμμής στο lockfile που παραλίγο να μας εμποδίσει να κυκλοφορήσουμε οτιδήποτε από όλα αυτά.

Αξιοπιστία
Μηχανική
Κυκλοφορία