Πώς να αφαιρέσετε την κληρονομιά ενιαίας τράπεζας από τα κιγκλιδώματα Monolith

Η κληρονομιά είναι εύκολη - μέχρι να αντιμετωπίσετε τεχνικά χρέη και φόρους.

Όταν γεννήθηκε ο βασικός κώδικας βάσης της Learn, πριν από πέντε χρόνια, η κληρονομιά Single Table (STI) ήταν αρκετά δημοφιλής. Η ομάδα Flatiron Labs την εποχή εκείνη πήγε όλα σε αυτό - χρησιμοποιώντας το για όλα από τις αξιολογήσεις και το πρόγραμμα σπουδών σε γεγονότα ζωοτροφών δραστηριότητα και το περιεχόμενο στο αυξανόμενο σύστημα μάθησης μας. Και αυτό ήταν υπέροχο - πήρε τη δουλειά. Έδωσε τη δυνατότητα στους εκπαιδευτές να προσφέρουν πρόγραμμα σπουδών, να παρακολουθούν την πρόοδο των σπουδαστών και να δημιουργούν μια συναρπαστική εμπειρία χρηστών.

Όμως, όπως έχουν επισημάνει πολλές θέσεις στο blog (αυτή, αυτή και αυτή, για παράδειγμα), η STI δεν έχει μεγάλη βαθμολογία, ειδικά καθώς τα δεδομένα αυξάνονται και οι νέες υποκατηγορίες αρχίζουν να κυμαίνονται ευρέως από τις υπερκλάσεις τους και μεταξύ τους. Όπως ίσως έχετε μαντέψει, το ίδιο συνέβη και με τον κώδικα μας! Το σχολείο μας επεκτάθηκε και υποστηρίξαμε όλο και περισσότερες λειτουργίες και τύπους μαθήματος. Με τον καιρό, τα μοντέλα άρχισαν να φουσκώνουν και να μεταλλάσσονται και δεν αντικατοπτρίζουν πλέον τη σωστή αφαίρεση για τον τομέα.

Ζούσαμε για λίγο στο χώρο αυτό, δίνοντας αυτόν τον κώδικα σε μια ευρεία αγκυροβόληση και την επιδιόρθω μόνο όταν ήταν απαραίτητο. Και τότε ήρθε ο χρόνος στο επαναστάτη.

Τους τελευταίους μήνες, ξεκίνησα μια αποστολή για να αφαιρέσω ένα ιδιαίτερα περίεργο παράδειγμα STI, το οποίο περιελάμβανε το μοντέλο κάπως αμφίβολο namedContent. Όσο πιο εύκολο είναι να ρυθμίσετε το STI αρχικά, είναι πραγματικά πολύ δύσκολο να το αφαιρέσετε.

Έτσι, σε αυτή τη θέση, θα καλύψω λίγο το STI, θα παράσχω κάποιο πλαίσιο για τον τομέα μου, θα περιγράψω το εύρος της εργασίας και θα συζητήσω τις στρατηγικές που χρησιμοποίησα για να αναπτύξω με ασφάλεια τις αλλαγές ενώ ελαχιστοποιούσα την επιφάνεια για σοβαρές ζημιές, της εφαρμογής μας.

Σχετικά με την κληρονομιά ενός τραπεζιού (STI)

Εν ολίγοις, η κληρονομιά Single Table in Rails σάς επιτρέπει να αποθηκεύετε πολλούς τύπους τάξεων στον ίδιο πίνακα. Στην ενεργή εγγραφή, το όνομα κλάσης αποθηκεύεται ως ο τύπος στον πίνακα. Για παράδειγμα, μπορεί να έχετε ένα Lab, Readme και Project όλα ζουν στον πίνακα περιεχομένων:

class Lab 

Σε αυτό το παράδειγμα, τα εργαστήρια, οι αναγνώσεις και τα έργα είναι όλα τα είδη περιεχομένου που θα μπορούσαν να συσχετιστούν με ένα μάθημα.

Το σχήμα του πίνακα περιεχομένων μας έμοιαζε κάπως έτσι, ώστε να μπορείτε να δείτε ότι ο τύπος αποθηκεύεται στον πίνακα.

create_table "περιεχόμενο", δύναμη:: cascade do | t |
  t.integer "curriculum_id",
  t.string "τύπου",
  t.text "markdown_format",
  t.string "title",
  t.integer "track_id",
  t.integer "github_repository_id"
τέλος

Προσδιορισμός του πεδίου της εργασίας

Περιεχόμενο που εκτείνεται σε όλη την εφαρμογή, μερικές φορές σύγχυση. Για παράδειγμα, αυτό περιγράφει τις σχέσεις στο μοντέλο μαθήματος.

μάθημα μαθήματος <Πρόγραμμα σπουδών
  has_many: περιεχόμενο, -> {τάξη (ordinal:: asc)}
  has_one: περιεχόμενο, foreign_key:: curriculum_id
  has_many: readmes, foreign_key:: curriculum_id
  has_one: lab, foreign_key:: curriculum_id
  has_one: readme, foreign_key:: curriculum_id
  has_many: assigned_repos, through:: περιεχόμενο
τέλος

Ταραγμένος? Έτσι ήταν και εγώ. Και αυτό ήταν μόνο ένα μοντέλο πολλών που έπρεπε να αλλάξω.

Έτσι, με τους λαμπρούς και ταλαντούχους συμπαίκτες μου (Kate Travers, Steven Nunez και Spencer Rogers), έλαβα ένα καλύτερο σχέδιο για να βοηθήσω να μειώσω τη σύγχυση και να διευκολύνω αυτό το σύστημα να επεκταθεί.

Ένα νέο σχέδιο

Η έννοια που το Content προσπαθούσε να εκπροσωπεί ήταν ένας διαμεσολαβητής μεταξύ ενός GithubRepository και ενός Μαθήματος.

Κάθε κομμάτι του "κανονικού" περιεχομένου μαθήματος συνδέεται με ένα αποθετήριο στο GitHub. Όταν τα μαθήματα δημοσιεύονται ή "αναπτύσσονται" σε μαθητές, κάνουμε ένα αντίγραφο αυτού του αποθετηρίου GitHub και δίνουμε στους μαθητές ένα σύνδεσμο σε αυτό. Η σύνδεση μεταξύ ενός μαθήματος και της αναπτυχθείσας έκδοσης ονομάζεται AssignedRepo.

Έτσι, υπάρχουν αποθήκες GitHub και στα δύο άκρα των μαθημάτων: η κανονική έκδοση και η αναπτυχθείσα έκδοση.

class Περιεχόμενο 
class AssignedRepo 

Σε ένα σημείο, τα μαθήματα μπόρεσαν να έχουν πολλαπλά κομμάτια περιεχομένου, αλλά στον σημερινό μας κόσμο αυτό δεν συμβαίνει πλέον. Αντ 'αυτού, υπάρχουν διάφορα είδη διδασκαλίας, τα οποία μπορούν να ερευνήσουν τους εαυτούς τους εξετάζοντας τα αρχεία που περιλαμβάνονται στις αντίστοιχες αποθήκες τους.

Έτσι, αυτό που αποφασίσαμε να κάνουμε ήταν να αντικαταστήσουμε το Περιεχόμενο με μια νέα έννοια που ονομάζεται CanonicalMaterial και να δώσουμε στον AssignedRepo άμεση αναφορά στο σχετικό μάθημά του αντί να περάσει από το Περιεχόμενο.

Διάγραμμα παλιού προς νέο, όπου οι κόκκινες διακεκομμένες γραμμές υποδεικνύουν διαδρομές που σημειώνονται για απόσπαση

Εάν αυτό ακούγεται συγκεχυμένο και σαν πολλή δουλειά, είναι επειδή είναι. Ωστόσο, το βασικό σενάριο είναι ότι έπρεπε να αντικαταστήσουμε ένα μοντέλο σε ένα πολύ μεγάλο κώδικα και κατέληξε να αλλάζει κάπου στη σφαίρα των 6000 γραμμών κώδικα.

Ωστόσο, το βασικό σενάριο είναι ότι έπρεπε να αντικαταστήσουμε ένα μοντέλο σε ένα πολύ μεγάλο κώδικα και κατέληξε να αλλάζει κάπου στη σφαίρα των 6000 γραμμών κώδικα.

Στρατηγικές για την εκ νέου επέμβαση και την αντικατάσταση της STI

Το νέο μοντέλο

Αρχικά, δημιουργήσαμε ένα νέο πίνακα που ονομάζεται canonical_materials και δημιούργησε το νέο μοντέλο και τις ενώσεις.

class CanonicalMaterial 

Προσθέσαμε επίσης ένα ξένο κλειδί του canonical_material_id στον πίνακα μαθημάτων, έτσι ώστε ένα Μάθημα να μπορεί να διατηρήσει μια αναφορά σε αυτό.

Στον πίνακα assign_repos, προσθέσαμε μια στήλη lesson_id.

Διπλή εγγραφή

Αφού τοποθετηθούν τα νέα τραπέζια και οι στήλες, αρχίσαμε να γράφουμε ταυτόχρονα στα παλιά τραπέζια και στα νέα, έτσι ώστε να μην χρειαζόμαστε περισσότερες από μία φορές να τρέχουμε ένα task backfill. Οποτεδήποτε προσπαθήσαμε να δημιουργήσουμε ή να ενημερώσουμε μια σειρά περιεχομένου, θα δημιουργούσαμε ή θα επικαιροποιούσαμε ένα canonical_material.

Για παράδειγμα:

lesson.build_content (
  'repo_name' => repo.name,
  'github_repository_id' => repo_id,
  'markdown_format' => repo.readme
)

lesson.canonical_material = repo.canonical_material
lesson.save

Αυτό μας επέτρεψε να θέσουμε τις βάσεις για την τελική κατάργηση του περιεχομένου.

Συμπλήρωση

Το επόμενο βήμα στη διαδικασία ήταν η συμπλήρωση των δεδομένων. Έγραψα καθήκοντα rake για να συμπληρώσουμε τα τραπέζιά μας και να εξασφαλίσουμε ότι υπήρχε ένα CanonicalMaterial για κάθε GithubRepository και ότι κάθε Μάθημα είχε ένα CanonicalMaterial. Και στη συνέχεια εκτελέσαμε τις εργασίες στον διακομιστή παραγωγής μας.

Σε αυτόν τον γύρο refactoring, προτιμήσαμε να έχουμε έγκυρα δεδομένα, ώστε να μπορέσουμε να κάνουμε ένα καθαρό διάλειμμα με τον κληροδοτημένο τρόπο να κάνουμε πράγματα. Μια άλλη βιώσιμη επιλογή, ωστόσο, είναι να γράψετε κώδικα που υποστηρίζει ακόμα παλαιότερα μοντέλα. Από την εμπειρία μας, είναι πιο συγκεχυμένη και δαπανηρή η διατήρηση κώδικα που υποστηρίζει την κληροδοτημένη σκέψη από ό, τι ήταν για την επαναπληρωμή και βεβαιωθείτε ότι τα δεδομένα είναι έγκυρα.

Από την εμπειρία μας, είναι πιο συγκεχυμένη και δαπανηρή η διατήρηση κώδικα που υποστηρίζει την κληροδοτημένη σκέψη από ό, τι ήταν για την επαναπληρωμή και βεβαιωθείτε ότι τα δεδομένα είναι έγκυρα.

Αντικατάσταση

Και τότε ξεκίνησε το διασκέδαση. Προκειμένου να γίνει η αντικατάσταση όσο το δυνατόν ασφαλέστερη, χρησιμοποιήσαμε σημαίες χαρακτηριστικών για να στείλουμε σκούρο κώδικα σε μικρότερες PR, πράγμα που μας επέτρεψε να δημιουργήσουμε έναν πιο γρήγορο βρόχο ανατροφοδότησης και να γνωρίζουμε νωρίτερα αν τα πράγματα έσπασαν. Χρησιμοποιήσαμε το gem rollout, το οποίο χρησιμοποιούμε επίσης για την ανάπτυξη τυπικών λειτουργιών, για να το κάνουμε αυτό.

Τι να ψάξετε

Ένα από τα πιο δύσκολα μέρη της αντικατάστασης ήταν ο πλήθος των πραγμάτων που έπρεπε να αναζητήσουμε. Η λέξη "περιεχόμενο" είναι, δυστυχώς, υπερ-γενική, οπότε ήταν αδύνατο να γίνει μια απλή, παγκόσμια αναζήτηση και αντικατάσταση, γι 'αυτό έχω την τάση να κάνω μια αναζήτηση με μεγαλύτερη εμβέλεια προσπαθώντας να υπολογίσω τις παραλλαγές.

Κατά την κατάργηση του STI, αυτά είναι τα πράγματα που πρέπει να αναζητήσετε:

  • Οι μοναδικές και πληθυντικές μορφές του μοντέλου, συμπεριλαμβανομένων όλων των υποκατηγοριών του, των μεθόδων, των μεθόδων χρησιμότητας, των συσχετίσεων και των ερωτημάτων.
  • Σταθερά ερωτήματα SQL
  • Ελεγκτές
  • Serializers
  • Προβολές

Για παράδειγμα, για περιεχόμενο, αυτό σήμαινε την αναζήτηση:

  • : περιεχόμενο - για ενώσεις και ερωτήματα
  • : περιεχόμενα - για συσχετίσεις και ερωτήματα
  • .joins (: περιεχόμενα) - για ερωτήματα εγγραφής, τα οποία πρέπει να ληφθούν υπόψη από την προηγούμενη αναζήτηση
  • Περιλαμβάνει (: περιεχόμενα) - για την πρόκληση φόρτωσης των δεύτερων παραγγελιών, οι οποίες πρέπει επίσης να εμπλακούν στην προηγούμενη αναζήτηση
  • περιεχόμενο: - για ένθετα ερωτήματα
  • περιεχόμενα: - πάλι, περισσότερα ένθετα ερωτήματα
  • content_id -για ερωτήματα απευθείας από id
  • κλήσεις με βάση τη μέθοδο
  • .contents - κλήσεις μεθόδου συλλογής
  • .build_content - μέθοδος χρησιμότητας που προστέθηκε από τη συσχέτιση has_one και ανήκει στην
  • .create_content - μέθοδος χρησιμότητας που προστέθηκε από τη συσχέτιση has_one και ανήκει στην
  • .content_ids - μέθοδος χρησιμότητας που προστέθηκε από την ένωση has_many
  • Περιεχόμενο - το ίδιο το όνομα της κλάσης
  • περιεχόμενα - η απλή συμβολοσειρά για τυχόν αναφορές σε hardcoded ή ερωτήματα SQL

Πιστεύω ότι είναι μια αρκετά ολοκληρωμένη λίστα για περιεχόμενο. Και έκανα το ίδιο για το εργαστήριο, το readme και το έργο. Μπορείτε να δείτε ότι επειδή το Rails είναι τόσο ευέλικτο και προσθέτει πολλές μεθόδους χρησιμότητας, είναι δύσκολο να βρείτε όλα τα μέρη που ένα μοντέλο καταλήγει να χρησιμοποιείται.

Πώς να αντικαταστήσετε την υλοποίηση μετά την εύρεση όλων των καλούντων

Αφού εντοπίσετε όλοι οι τόποι κλήσεων του μοντέλου που προσπαθείτε να αντικαταστήσετε ή να καταργήσετε, μπορείτε να ξαναγράψετε τα πράγματα. Γενικά, η διαδικασία που ακολουθήσαμε ήταν

  1. Αντικαταστήστε τη συμπεριφορά της μεθόδου στον ορισμό ή αλλάξτε τη μέθοδο στον τόπο κλήσης
  2. Γράψτε τις νέες μεθόδους και καλέστε τους πίσω από τη σημαία ενός χαρακτηριστικού στον ιστότοπο κλήσεων
  3. Διαχωρίστε τις εξαρτήσεις από τις συσχετίσεις με τις μεθόδους
  4. Αυξήστε τα σφάλματα πίσω από τη σημαία ενός χαρακτηριστικού εάν δεν είστε σίγουροι για μια μέθοδο
  5. Ανταλλάξτε αντικείμενα που έχουν την ίδια διεπαφή

Ακολουθούν παραδείγματα κάθε στρατηγικής.

1α. Αντικαταστήστε τη συμπεριφορά ή το ερώτημα της μεθόδου

Ορισμένες από τις αντικαταστάσεις είναι αρκετά απλές. Θέσατε τη σημαία χαρακτηριστικού στη θέση της για να πείτε "καλέστε αυτόν τον κώδικα αντί του άλλου κώδικα όταν αυτή η σημαία είναι ενεργοποιημένη".

Επομένως, αντί να ερωτάμε με βάση το περιεχόμενο, εδώ διερωτάμε με βάση το canonical_material.

1b. Αλλάξτε τη μέθοδο στην τοποθεσία κλήσης

Μερικές φορές, είναι ευκολότερο να αντικατασταθεί η μέθοδος στον ιστότοπο κλήσεων για την τυποποίηση των μεθόδων που ονομάζονται. (Πρέπει να εκτελέσετε τη δοκιμαστική σουίτα ή / και να γράψετε δοκιμές όταν το κάνετε αυτό.) Με αυτόν τον τρόπο μπορείτε να ανοίξετε τη διαδρομή για περαιτέρω refactoring.

Αυτό το παράδειγμα δείχνει πώς να σπάσει η εξάρτηση από τη στήλη canonical_id, η οποία σύντομα δεν θα υπάρχει πλέον. Παρατηρήστε ότι αντικαταστήσαμε τη μέθοδο στον ιστότοπο κλήσεων χωρίς να το βάζουμε πίσω από τη σημαία ενός χαρακτηριστικού. Κάνοντας αυτό το refactoring, παρατηρήσαμε ότι βγάζαμε το canonical_id σε περισσότερα από ένα μέρη, έτσι ολοκληρώσαμε τη λογική για να το κάνουμε με μια άλλη μέθοδο που μπορούσαμε να συνδέσουμε με άλλα ερωτήματα. Η μέθοδος στον ιστότοπο κλήσεων άλλαξε, αλλά η συμπεριφορά δεν άλλαξε μέχρι να ενεργοποιηθεί η λειτουργία σημαίας.

2. Γράψτε νέες μεθόδους και καλέστε τους πίσω από τη σημαία ενός χαρακτηριστικού στο χώρο κλήσεων

Αυτή η στρατηγική σχετίζεται με την αντικατάσταση της μεθόδου, μόνο σε αυτό, γράφουμε μια νέα μέθοδο και την ονομάζουμε πίσω από τη σημαία ενός χαρακτηριστικού στον τόπο κλήσης. Ήταν ιδιαίτερα χρήσιμο για μια μέθοδο που κλήθηκε μόνο σε ένα μέρος. Μας επέτρεψε επίσης να δώσουμε στη μέθοδο μια καλύτερη υπογραφή - πάντα χρήσιμη.

3. Διαχωρίστε τις εξαρτήσεις από τις συσχετίσεις με τις μεθόδους

Σε αυτό το επόμενο παράδειγμα, ένα κομμάτι έχει πολλά εργαστήρια. Επειδή γνωρίζουμε ότι η συσχέτιση has_many προσθέτει μεθόδους χρησιμότητας, αντικαταστήσαμε αυτήν που καλείται συνήθως και καταργείται η γραμμή has_many: labs. Αυτή η μέθοδος συμμορφώνεται με την ίδια διασύνδεση, οπότε οτιδήποτε καλούσε τη μέθοδο πριν ενεργοποιηθεί η λειτουργία θα συνεχίσει να λειτουργεί.

4. Αυξήστε τα σφάλματα πίσω από τη σημαία ενός χαρακτηριστικού εάν δεν είστε βέβαιοι για μια μέθοδο

Υπήρξαν κάποιες φορές που δεν ήμασταν σίγουροι αν χάσαμε έναν τόπο κλήσης. Έτσι, αντί για απλώς σκληρές μεθόδους αφαίρεσης στην αρχή, δημιουργήσαμε σκόπιμα σφάλματα, έτσι ώστε να μπορούσαμε να τα πιάσουμε κατά τη διάρκεια της χειροκίνητης δοκιμαστικής φάσης. Αυτό μας έδωσε έναν καλύτερο τρόπο για να εντοπίσουμε πού κλήθηκε μια μέθοδος.

5. Αλλαγή σε αντικείμενα που έχουν την ίδια διεπαφή

Επειδή θέλαμε να απαλλαγούμε από την εργαστηριακή ένωση, ξαναγράψαμε την εφαρμογή του εργαστηρίου; μέθοδος. Αντί να ελέγξουμε για την παρουσία ενός εργαστηριακού δίσκου, αλλάξαμε το κανόνα_μέσα, αναθέσαμε την κλήση και κάναμε το αντικείμενο να ανταποκρίνεται στην ίδια μέθοδο.

Αυτές ήταν οι πιο χρήσιμες στρατηγικές για το σπάσιμο των εξαρτήσεων και την εναλλαγή σε νέα αντικείμενα σε όλο το μονολιθικό μας Rails. Μετά την ανασκόπηση των εκατοντάδων ορισμών και των τόπων κλήσεων, τα αντικαταστήσαμε ή τα ξαναγράψαμε ένα προς ένα. Είναι μια κουραστική διαδικασία που δεν επιθυμώ σε κανέναν, αλλά ήταν τελικά εξαιρετικά χρήσιμο για να κάνουμε τον κώδικα μας πιο ευανάγνωστο και για την αφαίρεση παλαιού κώδικα που καθόταν γύρω από να μην κάνει τίποτα. Χρειάστηκαν αρκετές απογοητευτικές εβδομάδες για να φτάσουμε στο τέλος, αλλά μόλις αντικαταστήσαμε την πλειοψηφία των αναφορών, ξεκινήσαμε να κάνουμε χειροκίνητες δοκιμές.

Δοκιμές & Χειροκίνητες δοκιμές

Επειδή οι αλλαγές επηρέασαν τις λειτουργίες σε ολόκληρο το σύνολο των κωδικών, μερικές από τις οποίες δεν ελέγχονταν, ήταν δύσκολο να έχουμε QA με βεβαιότητα, αλλά κάναμε το καλύτερο δυνατό. Πραγματοποιήσαμε χειρωνακτικές δοκιμές στον εξυπηρετητή μας QA, ο οποίος έφερε πολλά σφάλματα και περιστατικά. Και μετά προχωρήσαμε και για πιο κρίσιμα μονοπάτια, έγραψα νέα τεστ.

Roll Out, Go Live & Clean Up

Αφού περάσαμε το QA, γυρίσαμε τη σημαία του χαρακτηριστικού μας και αφήσαμε το σύστημα να εγκατασταθεί. Αφού ήμασταν βέβαιοι ότι ήταν σταθερό, καταργήσαμε τις σημαίες χαρακτηριστικών και τις παλιές διαδρομές κώδικα από το βασικό κώδικα. Αυτό, δυστυχώς, ήταν δυσκολότερο από το αναμενόμενο, καθώς συνεπαγόταν επανεγγραφή πολλών δοκιμαστικών προγραμμάτων, κυρίως εργοστασίων που βασίζονταν σιωπηρά στο μοντέλο περιεχομένου. Σε εκ των υστέρων, αυτό που θα μπορούσαμε να κάνουμε ήταν να γράψουμε δύο σειρές δοκιμασιών κατά τη διάρκεια της refactoring, μία για τον τρέχοντα κώδικα και μία για τον κώδικα πίσω από μια σημαία χαρακτηριστικών.

Ως τελευταίο βήμα, που έρχεται ακόμα, πρέπει να δημιουργήσουμε αντίγραφα ασφαλείας και να αφήσουμε τα αχρησιμοποίητα τραπέζια μας.

Και αυτό, φίλοι, είναι ένας τρόπος να απαλλαγείτε από την απλή κατανομή της μονοκατοικίας στον μονόλιθο Rails σας. Ίσως αυτή η μελέτη περίπτωσης να σας βοηθήσει επίσης.

Έχετε άλλους τρόπους αφαίρεσης του STI ή του refactoring; Είμαστε περίεργοι να το ξέρουμε. Ενημερώστε μας στα σχόλια.

Επίσης, προσλαμβάνουμε! Γίνετε μέλος της ομάδας μας. Είμαστε δροσεροί, υπόσχομαι.

Πόροι και πρόσθετη ανάγνωση

  • Οδηγός Κληρονομιάς
  • Πώς και πότε να χρησιμοποιήσετε την κληρονομιά ενός τραπεζιού σε κιγκλιδώματα από τον Eugene Wang (Flatiron Grad!)
  • Επανεξέταση της εφαρμογής Rails μας από την κληρονομιά ενός τραπεζιού
  • Μονοακουστική κληρονομιά εναντίον πολυμορφικών ενώσεων σε ράγες
  • Μονάδα κληρονομίας με τη χρήση Rails 5.02

Για να μάθετε περισσότερα σχετικά με το Flatiron School, επισκεφτείτε τον ιστότοπο, ακολουθήστε μας στο Facebook και στο Twitter και μας επισκεφθείτε σε επερχόμενες εκδηλώσεις κοντά σας.

Το Flatiron School είναι υπερήφανο μέλος της οικογένειας WeWork. Ελέγξτε τα τεχνολογικά ιστολόγια αδελφών μας WeWork Technology and Making Meetup.