ToCode
رفتن به کانال در Telegram
1 420
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
-530 روز
آرشیو پست ها
1 420
החלק השני לא נראה מסובך אבל בגלל שטויות של אינדקסים הוא לקח לי הרבה יותר זמן ממה שהיה צריך. המשימה הפעם היא לסובב את הלוח לכל 4 הכיוונים ואז להמשיך לעשות את זה שוב ושוב סך הכל 1000000000 פעמים (אני אפילו לא יודע איך לקרוא את המספר הזה), ואז לחשב את סכום העומס של הסלעים.
יש פה שני אתגרים - קודם כל צריך לשכפל את הקוד שמטה את הלוח למעלה לכל 4 הכיוונים, ואחרי זה לחשוב איך להריץ אותו המון פעמים ולהגיע מתישהו לתוצאה. פה אפשר לשים לב שבדרך כלל אחרי כמה פעמים שנטה את הלוח לכל הכיוונים הכדורים ייכנסו למסלול קבוע ולכן נרצה למצוא את המעגל הזה ואז לא נצטרך להריץ את הלולאה 1000000000 פעמים אלא רק בכפולה של גודל המעגל.
באתגר הראשון בטח שהיה עדיף לוותר על שכפולים ולהוציא את הפונקציונאליות המשותפת לפונקציות, אבל אנחנו פה באילוצי זמן וממילא לא נחזור לקוד הזה אז נקרא לזה גירסת הקופי פייסט:
def updateRowAsString(matrix: Map[(Int, Int), Char],
rowNumber: Int,
updater: String => String): Map[(Int, Int), Char] =
val row = matrix.collect {
case ((row, col), ch) if row == rowNumber => (col, ch)
}.toList.sortBy(_._1).map(_._2).mkString
val updated = updater(row)
updated.zipWithIndex.foldLeft(matrix) { case (matrix, (ch, idx)) =>
matrix.updated((rowNumber, idx), ch)
}
def tiltUp(column: String): String =
column
.split('#')
.map(_.sorted.reverse)
.mkString("#")
def tiltDown(column: String): String =
column
.split('#')
.map(_.sorted)
.mkString("#")
def tiltLeft(row: String): String =
row
.split('#')
.map(_.sorted.reverse)
.mkString("#")
def tiltRight(row: String): String =
row
.split('#')
.map(_.sorted)
.mkString("#")
def spinCycle(matrix: Map[(Int, Int), Char]): Map[(Int, Int), Char] =
val maxColumn = matrix.keys.maxBy(_._2)._2
val maxRow = matrix.keys.maxBy(_._1)._1
val s1 = 0.to(maxColumn).foldLeft(matrix) { (matrix, c) => updateColumnAsString(matrix, c, tiltUp) }
val s2 = 0.to(maxRow).foldLeft(s1) { (matrix, c) => updateRowAsString(matrix, c, tiltLeft) }
val s3 = 0.to(maxColumn).foldLeft(s2) { (matrix, c) => updateColumnAsString(matrix, c, tiltDown) }
val s4 = 0.to(maxRow).foldLeft(s3) { (matrix, c) => updateRowAsString(matrix, c, tiltRight) }
s4
בשביל האתגר השני כבר אפשר להסתפק בפונקציה רקורסיבית שמחפשת את המעגל:
@tailrec
def findCycle(matrix: Map[(Int, Int), Char],
iterate: (Map[(Int, Int), Char] => Map[(Int, Int), Char]),
seen: List[Map[(Int, Int), Char]] = List()): List[Map[(Int, Int), Char]] =
if (seen.contains(matrix)) {
seen :+ matrix
} else {
findCycle(iterate(matrix), iterate, seen :+ matrix)
}
בקוד הפיתרון עצמו צריך לזכור שהמעגל כנראה לא מתחיל מהמיקום הראשון, כלומר יהיה לנו כמה פעמים שנטה את הלוח לכל הכיוונים ואז רק הכדורים ייכנסו למסלול מעגלי. ככה נראה הפיתרון בסקאלה:
@main
def day14part2(): Unit =
val matrix = parseInput(Source.fromResource("day14.txt"))
val maxColumn = matrix.keys.maxBy(_._2)._2
val maxRow = matrix.keys.maxBy(_._1)._1
val cycle :+ dup = findCycle(matrix, spinCycle)
val prefix = cycle.indexOf(dup)
val actualCycle = cycle.drop(prefix)
LazyList.iterate(matrix)(spinCycle).take(prefix + ((1000000000 - prefix) % actualCycle.size) + 1)
.last
.pipe(totalLoad)
.pipe(println)
יש לכם רעיונות אחרים? פיתרונות מעניינים בשפות אחרות? מוזמנים תמיד לשתף בתגובות או בטלגרם.1 420
פיתרון Advent Of Code 2023 יום 14 בסקאלה (חלק ראשון)
עוד שבוע מתחיל וסופי שבוע הם זמן מצוין לפתור עוד תרגיל של Advent Of Code. הפעם אנחנו ביום 14 ועדיין עם מטריצות.
התרגיל
נתון לוח עם סלעים, חלקם עגולים שמתגלגלים וחלקם מרובעים שלא זזים. ככה זה נראה:
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
עכשיו אנחנו מטים את הלוח קצת למעלה, אז כל הסלעים העגולים מתגלגלים למעלה ואנחנו מקבלים את הצורה הזאת:
OOOO.#.O..
OO..#....#
OO..O##..O
O..#.OO...
........#.
..#....#.#
..O..#.O.O
..O.......
#....###..
#....#....
המרובעים לא זזו כמו שדיברנו והעגולים התגלגלו עד שנעצרו בגלל סלע אחר.
עכשיו נסמן כל שורה במספר ונרצה לדעת כמה כדורים יש בכל שורה. בשביל להגיע למספר אחד כופלים את המספר של השורה במספר הכדורים שבה וסוכמים את כל התוצאות. עם המספרים זה נראה ככה:
OOOO.#.O.. 10
OO..#....# 9
OO..O##..O 8
O..#.OO... 7
........#. 6
..#....#.# 5
..O..#.O.O 4
..O....... 3
* ....###.. 2 *
* ....#.... 1 *
יש 5 כדורים בשורה של 10 אז זה נותן 50, עוד שני כדורים בשורה של 9 שמוסיפים 18, וככה ממשיכים ומחברים עד שמקבלים את הסכום 136.
המשימה היא לכתוב תוכנית שמקבלת את המטריצה ומחשבת את הסכום אחרי כל הטלטלה.
פיתרון בסקאלה
הדבר הראשון שקופץ לעין כאן הוא שההזזה קורית לכל טור בנפרד, ושבתוך הטור יחסית קל להבין איך הוא יראה אחרי ההטיה למעלה - פשוט חותכים את הטור בכל סולמית, מזיזים את כל העיגולים להתחלה והנקודות לסוף ואז מחברים חזרה. בסקאלה זה יהיה פשוט:
"OO.O.O..##".split('#').map(_.sorted.reverse).mkString("#")
ואז הקוד לטיפול בהטיה למעלה ייקח עמודה עמודה, יזיז את הסלעים בה למעלה ובסוף יחשב את הסכום לפי החישוב בתרגיל. זה הסיפור בסקאלה:
import scala.annotation.tailrec
import scala.io.Source
import scala.util.chaining.*
object aoc2023day14 {
val demoInput: String =
"""O....#....
|O.OO#....#
|.....##...
|OO.#O....O
|.O.....O#.
|O.#..O.#.#
|..O..#O..O
|.......O..
|#....###..
|#OO..#....""".stripMargin
def parseInput(input: Source): Map[(Int, Int), Char] =
input
.getLines()
.zipWithIndex
.collect {
case (line: String, index: Int) => line.toList.zipWithIndex.map((ch, column) => (index, column, ch))
}
.flatten
.flatMap { case (row, column, ch) => Map((row, column) -> ch) }
.toMap
def printMatrix(matrix: Map[(Int, Int), Char]): Unit =
val maxRow = matrix.keys.maxBy(_._1)._1
val maxColumn = matrix.keys.maxBy(_._2)._2
0.to(maxRow).foreach { row =>
0.to(maxColumn).foreach { col =>
print(matrix((row, col)))
}
println()
}
def updateColumnAsString(matrix: Map[(Int, Int), Char],
columnNumber: Int,
updater: (String) => String): Map[(Int, Int), Char] =
val column = matrix.collect {
case ((row, col), ch) if col == columnNumber => (row, ch)
}.toList.sortBy(_._1).map(_._2).mkString
val updatedColumn = updater(column)
updatedColumn.zipWithIndex.foldLeft(matrix) { case (matrix, (ch, idx)) =>
matrix.updated((idx, columnNumber), ch)
}
def tiltUp(column: String): String =
column
.split('#')
.map(_.sorted.reverse)
.mkString("#")
def totalLoad(matrix: Map[(Int, Int), Char]): Long =
val maxRow = matrix.keys.maxBy(_._1)._1
matrix.map {
case ((row, col), 'O') => maxRow + 1 - row
case ((row, col), _) => 0
}.sum
@main
def day14part1(): Unit =
val matrix = parseInput(Source.fromResource("day14.txt"))
val maxColumn = matrix.keys.maxBy(_._2)._2
0.to(maxColumn).foldLeft(matrix) {(matrix, c) =>
updateColumnAsString(matrix, c, tiltUp)
}.pipe(totalLoad)
.pipe(println)
}
מחשבות על חלק 21 420
סטייט ובחירת קומפוננטות ריאקט
ריאקט התחילה בתור ספריית UI ולכן זה לא היה מוזר לחשוב על קומפוננטה בתור "החלק בעמוד שמשתנה יחד". הסטייט של הקומפוננטה הוא המידע ששינוי שלו מוביל לשינוי ב UI ואם יש מידע שצריך להשפיע על כמה קומפוננטות נשמור אותו באיזשהו מקום חיצוני (פלאקס, זוכרים?) ונחבר את הקומפוננטות לשם. הדבר החשוב בחלוקה לקומפוננטות היה להיות מסוגלים לעשות Reasoning על החלקים בעמוד. להיות מסוגלים להבין למה הופיע עכשיו הסימן שיש הודעה חדשה.
אבל כל זה היה הגיוני ב 2015. אולי ב 2018.
היום ובמיוחד מאז Hooks אנחנו חושבים על קומפוננטה בתור הדבר בעמוד שחוזר על עצמו בכל מיני מקומות והקשרים. בתור ה Building Block של ארכיטקטורה מבוססת קומפוננטות. משהו שיש לו לוגיקה, משהו שמזיז דברים ביישום. ריאקט, למרות מה שהם כותבים בכותרת, היא כבר לא ספריה רק ל UI או לפחות אנשים לא משתמשים בה רק באופן הזה.
וכך נולד הקונפליקט- האם ליצור קומפוננטות לפי סמיכות מבחינת סטייט או לפי ממשק חיצוני ואפשרות לשימוש חוזר? לפעמים שני השיקולים האלה הולכים יחד, לפעמים הם מתנגשים, ובכל מקרה צורת החשיבה שמובילה לכל אחד שונה.
להשתמש בסטייט בתור קו מנחה לבחירת קומפוננטות זה קשה כי האינטואיציה שלנו כמתכנתים מחפשת "לסדר" את הקוד לאלמנטים לוגיים. במקרה של ריאקט זה שווה את המאמץ לחשוב כמו מפתחי UI.
1 420
כמה מתכנתים צריך בשביל להתחבר ל S3?
מתכנת חכם אחד בנה תשתית משוגעת לחיבור ל S3 בתור POC. הוא רצה להראות ששמירת הקבצים על S3 תוכל לשפר את הביצועים במערכת ולאפשר גדילה אופקית. הפרויקט אומנם נקבר בגלל חילופי הנהלה אבל הבסיס של הקוד כבר הוכנס למערכת.
מתכנת חכם שני קיבל בקשה מלקוח לחבר לחשבון שלו את הקבצים שכבר שמורים ללקוח על S3. אצלו בצוות מתכנתים קיבלו קרדיט לפי כמה באגים הם פותרים ולכן היה חשוב לגרום לדברים לעבוד מהר ולהמשיך לסיפור הבא. הוא ידע שיש איזה תשתית בסיסית איפשהו אבל למי היה זמן להבין איך זה עובד אז הוא בנה מנגנון חדש וממוקד שעבד ממש בסדר.
מתכנת חכם שלישי גילה שמישהו כבר בנה ספרייה שמתחברת עם S3 וגם בונה אבסטרקציה כללית מעל איחסון קבצים בענן (כך שאפשר יהיה לעבור מהר לעננים אחרים). הוא החליט לממש באמצעותה חיבור ל S3 עבור פיצ'ר חדש אבל לא רצה להסתכן בלשבור קוד ישן ולא בדוק, ולכן לא ביצע מיגרציה מלאה.
מנהל חכם אחד שמע שהרבה לקוחות רוצים לאחסן קבצים על S3 וידע שעכשיו Micro Services הם ה-דבר, אז הוא מצא צוות באוקראינה שיבנו מיקרו סרביס עם חיבור ל S3. נכון שהסרביס כתוב ב Erlang אבל הבטיחו לו שהביצועים יהיו מצוינים ושהאוקראינים יוכלו לפתור כל בעיה בלי לגעת בשאר המערכת.
מתכנת חכם רביעי היה צריך לעדכן את מנגנון אחסון הקבצים של הלקוחות. האוקראינים לא זמינים כבר שנתיים וכאן אף אחד לא יודע Erlang. את הספריה שהשלישי הכניס הוא לא הכיר וממילא היא כבר לא נתמכת ושני המנגנונים האחרים לא היו מספיק גנריים או קלים לשימוש. מזל שבדיוק יצאה ספריית חיבור חדשה ל S3 עם ממשק חדשני ואבטחת מידע משופרת.
כך נראה חוב טכני. כך נראית מערכת שכבר קשה להתקדם עם פיצ'רים כי כל שינוי שובר משהו אחר. זה הפיך אבל דורש עבודה והיום הוא בדיוק יום מצוין להתחיל לאחד מנגנונים.
1 420
לא ככה משתמשים בזה
אין לי ספק שלסטיב ג'ובס היו כוונות טובות כשהוא האשים את המשתמשים שמחזיקים לא נכון את הטלפון שלו. הוא ראה את כל השעות שהם בילו במעבדות בבדיקות, את כל הנסיינים שניסו כל פונקציה בטלפון, את הבדיקות האוטומטיות, את הלוגים, את מנגנוני ההגנה והרגולציות.
והנה המוצר יוצא לשוק והמשתמשים עושים איתו דברים שאף אחד לא חשב עליהם בזמן הפיתוח.
מאיפה יש להם את החוצפה?
וזה לא רק האייפון...
איך הם לוחצים על כפתור פעמיים לפני שסיימתי לטפל בלחיצה הראשונה?
למה הם שומרים את הסטייט כל כך גבוה בעץ הקומפוננטות? הרי אמרתי לכם שזה יביא ליותר מדי רנדרים.
למה הם מנסים לתשאל את בסיס הנתונים בלי אינדקסים? ברור שזה יעבוד לאט.
מה פתאום הם בונים שאילתת SQL מקלט שמגיע בטופס? הרי אמרתי להם שצריך לנקות את הקלט לפני שמשתמשים בו.
"לא ככה משתמשים בזה" לא עובד. אנשים ימשיכו להשתמש לא נכון במוצרים שלנו, וימשיכו לבוא בטענות. כדאי לזכור:
1. בתור משתמשים חשוב לדעת איך היוצר המקורי תכנן שנשתמש במוצר. לא חייבים להשתמש בפריימוורק לפי החוקים אבל חשוב להכיר ולהבין אותם כדי להבין איך בדיוק זה יישבר כשנחזיק את זה לא נכון.
2. בתור יוצרים עלינו להיות מוכנים לביקורת על המוצרים שלנו ויש לנו את הבחירה - לתקן או להתעקש. ריאקט אומנם מתעקשים על מנגנון ניהול הסטייט שלהם אבל מנגנוני ORM פתרו כמעט לגמרי את הבעיה שאנשים בונים שאילתות SQL בצורה ידנית ולא מאובטחת. גם כשמחליטים "להתעקש" אפשר לוודא שהמשתמשים שלנו מבינים מה הם עושים לא בסדר ואיך מתקנים. בריאקט בגלל זה דואגים להדפיס אזהרה כל פעם שאנחנו מרנדרים קומפוננטות בלולאה בלי key. בסיס הנתונים JanusGraph מדפיס לי אזהרה ללוג כל פעם שאני שולח שאילתה ואין לו אינדקס מתאים בשבילה.
בסוף ברור שזה סיפור של תיאום ציפיות - אף אחד לא מצפה לזרוק את האייפון מקומה רביעית ושימשיך לעבוד, אבל אנחנו כן מצפים שנוכל להחזיק אותו איך שנרצה ועדיין להוציא שיחות. ככל שנתקשר טוב יותר עם המשתמשים שלנו ונבין את הציפיות שלהם נוכל לבנות מוצרים שיתאמו ואף יתעלו מעל אותן ציפיות.
1 420
למה אני לא מתלהב מ React Forget
אם עדיין לא שמעתם ככל הנראה ריאקט 19 תשוחרר עם תמיכה ב React Forget הקומפיילר החדש מבית ריאקט שאמור להוסיף useMemo אוטומטית לתוכניות שלנו. הנימוקים שלהם לפיתוח הפיצ׳ר (מתוך הפוסט) היו-
1. כתיבת useMemo ידנית מלכלכת את הקוד.
2. קל לטעות כשצריך להחליט ידנית למה לעשות Memoization.
3. תוספת הקוד מעמיסה על התחזוקה.
הנה משפט המפתח מתוך הפוסט-
> Our vision is for React to automatically re-render just the right parts of the UI when state changes, without compromising on React’s core mental model.
ומהו אותו מודל מנטלי בליבה של ריאקט? גם פה הם עונים שהממשק הוא בסך הכל פונקציה על מצב האפליקציה, או במילים אחרות שמספיק להסתכל על פונקציית הקומפוננטה ועל הסטייט כדי לדעת מה יופיע על המסך.
ועכשיו אפשר לחזור לכותרת. עם כל ההסברים של החברים בריאקט, איך קרה שהבלוגר הזה לא הכי מתלהב מריאקט-שוכח. הנה הסיבות שלי-
1. אין בזה צורך, או יותר נכון הצורך היחיד הוא יחסי ציבור. בכל פרויקט ריאקט שעבדתי עליו בעיות הביצועים נוצרו בגלל חוסר הבנה של איך ריאקט עובד ועוד יותר גרוע חוסר הבנה של המרחק בין קוד צד שרת לקוד צד לקוח. ברוב מוחלט של הסיטואציות אפשר היה להחליף את useMemo בארגון אחר של הקוד הקומפוננטות ולפתור את בעיות הביצועים (וגם לקבל קוד טוב יותר).
2. הוספת "קוד אוטומטי" רק מסבכת את ההבנה והתחזוקה. קוד שאני לא רואה עדיין נמצא שם, ואני צריך לזכור אותו ולחשוב עליו כל הזמן.
3. הוספת מנגנונים כמו React Forget רק מרחיקה אותנו מפיתרון הבעיות האמיתיות של ריאקט. במובן הזה סיגנלים היו יכולים להיות תוספת הרבה יותר מועילה ומעניינת.
האם React Forget יגרום לאפליקציות שלנו לעבוד מהר יותר? כנראה שכן. האם הוא יביא איתו בעיות חדשות שאף אחד לא היה צריך בשביל שיפור מינורי בביצועים? כנראה שגם כן.
1 420
מאיפה הגיעה ה Z בסוף הזמן?
בשביל לקחת בסקאלה (או Java) את השעה הנוכחית ולשמור אותה כמחרוזת אוכל להשתמש בקוד הבא:
import java.time.*
import java.time.format.DateTimeFormatter
LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME)
והמחרוזת שתתקבל עשויה להיראות כך:
"16:51:44.748375"
אבל כשמסתכלים בבסיסי נתונים וברשת אנחנו נתקלים במחרוזות דומות שמסתיימות באות Z לדוגמה:
14:54:10.140198Z
למה הם התכוונו? מאיפה ה Z הגיע? ואיך אני גם מקבל אחד?
אז בואו נתחיל בקוד בשביל ליצור מחרוזת זמן עם ה Z בסוף אני לא צריך לשרשר אותו ידנית אלא להשתמש בקוד מקביל שכולל התיחסות לאזור זמן:
OffsetTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ISO_OFFSET_TIME)
ה Z מסמן שזוהי תווית זמן באזור זמן UTC. אזורי זמן אחרים כבר מקבלים סימון יותר ברור לדוגמה עבור ישראל נקבל:
scala> OffsetTime.now(ZoneId.of("Israel")).format(DateTimeFormatter.ISO_OFFSET_TIME);
val res5: String = 16:57:24.769026+02:00
וכן היה אפשר במקום ה Z לכתוב משהו כמו פלוס אפס, אבל כנראה שזה לא מספיק מבלבל בשביל מי שכותב תקנים לדברים כאלה.
כשאנחנו בונים מערכת שצריכה לתמוך בכמה אזורי זמן יש לנו שתי אפשרויות מרכזיות - האחת (פשוטה יותר ולדעתי מומלצת), להחליט על אזור זמן בו נשמור את כל השעות במערכת ולשמור בבסיס הנתונים שעה ללא אזור זמן. במצב כזה כל פעם לפני שמירה בבסיס הנתונים נרצה להמיר את השעה שיש לנו לשעה באזור הזמן של בסיס הנתונים, ובכל קריאה נרצה לבצע המרה הפוכה.
אופציה שניה יותר מורכבת היא לשמור את המידע כולל אזור הזמן בבסיס הנתונים. בשביל זה אנחנו יכולים להפוך את השעה למחרוזת הכוללת אזור זמן (עם ה Z בסוף ל UTC, או עם Offset לאזורי זמן אחרים). במצב כזה לא נדרשת המרה אבל כל קוד המערכת יהיה מורכב יותר כי תמיד צריך לחשוב באיזה אזור זמן אנחנו ולעבוד עם סט המחלקות המתאים.1 420
על גיטהאב, חתימה על קומיטים ולמי אכפת
בואו נתחיל בניסוי (רק לאמיצים שבחבורה) - משורת הפקודה שנו את שמכם ואת כתובת הדואר האלקטרוני שגיט חושב שיש לכם עם הפקודות:
git config --replace-all user.name elite-hacker
git config --replace-all user.email hacker@theboss.com
ואז הכניסו קומיט למערכת. יכול להיות גם ריק, והפעילו git log. מה הפרטים שאתם רואים בקומיט?
ברירת המחדל של גיט במצב כזה היא לתת לכם את השליטה ולכן השם שיופיע בקומיט הוא אכן השם שבחרתם. אם עכשיו תדחפו את הקומיט למאגר מרוחק הקומיט יופיע שם עם השם המזויף שלכם.
הדרך של גיט לאמת את הזהות שלכם בקומיט היא חתימה על קומיטים. כאן יש מדריך איך לקנפג הכל בגיטהאב:
https://gist.github.com/troyfontaine/18c9146295168ee9ca2b30c00bd1b41e
אז למה אנחנו לא חותמים על קומיטים? (בפרויקטים פנימיים. בפרויקטי קוד פתוח הכל חתום אל דאגה). פה אני חושב שהאשמה על מנטליות ה"למי אכפת" ובפרט המחשבות:
1. מי ירצה לעשות קומיט בשם אחר? כולנו עובדים באותו ארגון. אין אצלנו אנשים רעים.
2. זה בטח מסובך להגדיר את כל הדברים, ואז יהיה איזה עדכון ופתאום דברים לא יעבדו אצלי. חבל על הזמן.
3. האקרים ממילא יודעים לזייף את החתימות שלנו אז בשביל מה להתאמץ.
אבל האמת היא שאנחנו פשוט לא אוהבים שמישהו מזיז את הגבינה שלנו. "כל החיים לא חתמתי על קומיטים ולא קרה כלום, אז למה להתחיל היום?". התשובה שלי לשלושת הסעיפים תהיה:
1. אי אפשר לדעת מי ירצה לעשות קומיט בשם אחר ובאיזה נסיבות, אולי זה אפילו יקרה בטעות. תמיד עדיף להיות קצת יותר מוגן.
2. דווקא די פשוט. המדריך בלינק שהדבקתי למעלה.
3. אז למה לעשות להם חיים קלים? שיתאמצו קצת ההאקרים.
עוד לא חותמים על קומיטים? היום הוא יום מצוין לקנפג את זה.1 420
טיפוס של GUI
בבניית מערכת צריך להתחיל מהממשק הגרפי או מבסיס הנתונים?
את הארכיטקטורה צריך לכתוב מלמעלה למטה (קודם האבסטרקציות ואחרי זה הקוד עצמו) או מלמטה למעלה (קודם הקוד וכשרואים חלקים משותפים מייצרים אבסטרקציה)?
את הבדיקות כותבים לפני או אחרי הקוד?
כבר לפני הרבה שנים הבנתי שאין תשובה אחת שמתאימה לכולם וזה בסדר - אבל בגלל שרוב האנשים שקראתי היו עקביים בדעות שלהם חשבתי בטעות שזה עניין של טעם אישי, כלומר אתה יכול להיות בצד של TDD או בצד של הבדיקות אחרי הקוד. אתה יכול להיות בצד שכותב רק בדיקות אינטגרציה (וחושב שבדיקות יחידה הן בזבוז זמן) או שאתה מאמין בבדיקות יחידה וכותב אותן על כל מודול. אתה יכול להיות טיפוס שכותב GUI קודם או טיפוס שכותב קודם את קוד צד השרת.
אני טיפוס של GUI הייתי חושב בלב וממשיך לכתוב את הממשק לפרויקט הבא. עד שיום אחד זה כבר לא עבד. שמכל מיני סיבות ואילוצים של הפרויקט כל פעם שניסיתי להתחיל מה GUI הקוד לא כתב את עצמו בכלל והגענו למבוי סתום. היפוך הכיוון עזר לקדם את הפרויקט אבל יותר מזה עזר לי לצאת מהקיבעון.
אני יכול להיות טיפוס של GUI או של Back End, אבל זה בכלל לא רלוונטי. יותר חשוב לשאול מה האילוצים של הפרויקט? מה מידת הגמישות שנצטרך בו? מה אנחנו יודעים לעומת מה עוד צריך לגלות? איזה חלקים צפויים להשתנות? ואולי בכלל אפשר לחלק את העבודה ולהתקדם במקביל?
שורה תחתונה השאלה היא לא איפה אנחנו מרגישים יותר נוח אלא מה טוב לפרויקט ומה יאפשר לנו יותר גמישות כשנצטרך אותה. מתכנתים טובים צריכים להיות מסוגלים לעבור בין הגישות ולהעדיף תמיד את טובת הפרויקט.
1 420
שלוש נקודות על SQL-ים מסובכים מדי
התוכנית המקורית שלי לפוסט סופשבוע היתה לפתור ולפרסם עוד יום של Advent Of Code, אבל מפה לשם את כל זמן הכתיבה בזבזתי על שאילתה מסובכת מדי לבסיס הנתונים שבסוף גם היא לא עבדה, ולכן במקום אני רוצה לשתף שלוש נקודות אליהן כדאי לשים לב כשהולכים מכות עם שאילתות (ובעצם עם פריימוורק באופן כללי)-
1. לפעמים זה שווה את המאמץ. יש שאילתות SQL שאחרי שמצליחים לכתוב אותן מבינים משהו חדש על SQL. כן יש דבר כזה Window Functions ואם צריך להשקיע עכשיו שעתיים בללמוד איך הן עובדות זה ממש שווה את ההשקעה.
2. לפעמים השאילתה באמת מסובכת מדי ולא שווה את המאמץ. ראינו כבר מספיק שאילתות מסובכות מדי שבסוף היה צריך לשכתב לשאילתות פשוטות יותר או להעביר חלק מהלוגיקה פנימה לקוד כי ה DB לא עמד בעומס.
3. לפעמים שאילתות מסובכות הן רמז לבעייה במבנה הטבלאות.
באותו רגע כשמנסים לגרום לשאילתה לעבוד אנחנו עדיין לא יודעים איזה משלושת הדרכים תהיה הטובה ביותר וקל להיתקע על הפיתרון הראשון שנראה הגיוני. בואו ננסה לזכור שתמיד יש עוד דרכים ואולי הפיתרון הוא צעד אחורה והמשך בכיוון אחר.
1 420
מה עושים במקום התיעוד הלא רלוונטי
אה זה מה Readme? תתעלם הוא לא מעודכן
ההערה הזאת ישנה את יכולה להתעלם, שינינו הרבה קוד מאז
תתעלם מהבדיקות שנכשלות צריך לסדר אותן
עכשיו אני יודע אין זמן לעדכן ולא נעים למחוק, אז מה עושים עם כל התיעוד הלא רלוונטי הזה? איך נדאג לזה שהוא לא ימשיך לבלבל אחרים? טריק אחד שהתחלתי לנסות זה כן למחוק את הדברים ובמקומם להשאיר הערה בקוד עם מספר הקומיט שבו אותו תיעוד ישן כן היה במערכת. ככה מי שיגיע למקום יצטרך לעבוד כדי למצוא את אותו תיעוד לא רלוונטי, וגם יהיה לכולם ברור מה מצבו של אותו תיעוד. כשמישהו כן ימצא את הזמן לתקן יהיה קל למצוא את הגירסה האחרונה ולהמשיך משם.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
