30 KiB
30 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Application | Start UI from Application subclass | JavaFX apps should bootstrap the primary Stage through Application.start() | Extend Application and configure Scene in start() | Create UI from a random main method without launching JavaFX | public class App extends Application { public void start(Stage stage) { stage.setScene(new Scene(root)); stage.show(); } } | new Stage().show() | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/application/Application.html |
| 3 | 2 | Threading | Keep work off the FX Application Thread | Long-running work blocks rendering and input when executed on the UI thread | Use Task or Service for background work | Run network database or file work in button handlers | Task<List<Item>> task = new Task<>() { protected List<Item> call() { return repo.load(); } }; new Thread(task).start(); | loadLargeFile(); table.setItems(items); | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html |
| 4 | 4 | Threading | Bind progress to background tasks | Task exposes progress and message properties for responsive feedback | Bind ProgressBar and Label to task properties | Poll progress manually or leave users without feedback | progress.progressProperty().bind(task.progressProperty()); status.textProperty().bind(task.messageProperty()); | while(running) progress.setProgress(x); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html |
| 5 | 5 | FXML | Use FXML for stable declarative layouts | FXML keeps view structure readable for screens with many controls | Place layout in FXML and behavior in controller | Build large screens entirely in one Java method | <VBox spacing="12" xmlns:fx="http://javafx.com/fxml" fx:controller="app.MainController"> | VBox root = new VBox(); root.getChildren().add(... 200 lines ...); | Medium | https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html |
| 6 | 6 | FXML | Keep controllers focused on view behavior | Controllers should coordinate controls and delegate business logic to services | Inject services or call application services from controller | Put database queries and domain rules directly in controller | public void save() { customerService.save(form.toCommand()); } | public void save() { DriverManager.getConnection(...); } | High | https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXML.html |
| 7 | 10 | CSS | Use design tokens through looked-up colors | Looked-up colors keep palettes consistent across controls | Define named colors on root and reuse them in CSS | Repeat hex values in every selector | .root { -brand-primary: #2563eb; } .button.primary { -fx-background-color: -brand-primary; } | .save { -fx-background-color: #2563eb; } .link { -fx-text-fill: #2563eb; } | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/doc-files/cssref.html |
| 8 | 11 | CSS | Avoid overusing inline effects | Expensive CSS effects and shadows can hurt desktop UI responsiveness | Use subtle shadows only on important elevated surfaces | Apply blur drop shadow and glow to every node | .dialog-card { -fx-effect: dropshadow(gaussian, rgba(0,0,0,.18), 16, 0, 0, 4); } | .table-row-cell { -fx-effect: dropshadow(gaussian, black, 20, .5, 0, 0); } | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/effect/package-summary.html |
| 9 | 12 | Layout | Choose layout panes by responsibility | Each pane solves a different layout problem and should be selected intentionally | Use BorderPane for app shell GridPane for forms VBox/HBox for simple stacks | Use absolute positioning for resizable app screens | BorderPane shell = new BorderPane(); shell.setTop(toolbar); shell.setCenter(content); | Pane root = new Pane(); button.setLayoutX(742); | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/package-summary.html |
| 10 | 13 | Layout | Prefer constraints over fixed coordinates | Responsive JavaFX layouts depend on constraints and grow priorities | Use hgrow vgrow column constraints and alignment | Hard-code pixel positions and sizes | GridPane.setHgrow(nameField, Priority.ALWAYS); column.setPercentWidth(50); | field.setPrefWidth(328); field.setLayoutX(120); | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/GridPane.html |
| 11 | 14 | Layout | Set sensible min pref and max sizes | Controls should resize predictably across windows and DPI settings | Use Region.USE_COMPUTED_SIZE and max widths intentionally | Lock every control to fixed width and height | button.setMaxWidth(Double.MAX_VALUE); VBox.setVgrow(table, Priority.ALWAYS); | button.setMinSize(96, 32); button.setMaxSize(96, 32); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/Region.html |
| 12 | 15 | Layout | Use spacing and padding consistently | Desktop UI needs scan-friendly rhythm and clear grouping | Set spacing padding and Insets through shared constants or CSS | Use inconsistent ad hoc gaps between controls | form.setHgap(12); form.setVgap(10); form.setPadding(new Insets(16)); | box.setSpacing(3); other.setSpacing(17); | Low | https://openjfx.io/javadoc/21/javafx.graphics/javafx/geometry/Insets.html |
| 13 | 16 | Controls | Use ObservableList for list controls | TableView ListView and ComboBox update automatically from observable collections | Back controls with FXCollections.observableArrayList() | Mutate plain lists and manually refresh controls | ObservableList<Customer> rows = FXCollections.observableArrayList(); table.setItems(rows); | List<Customer> rows = new ArrayList<>(); table.setItems((ObservableList) rows); | High | https://openjfx.io/javadoc/21/javafx.base/javafx/collections/ObservableList.html |
| 14 | 17 | Controls | Configure TableView cell value factories with properties | Table columns should observe stable JavaFX properties for updates | Expose StringProperty ObjectProperty or use ReadOnlyObjectWrapper | Return transient strings without observable support | nameCol.setCellValueFactory(data -> data.getValue().nameProperty()); | nameCol.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().toString())); | Medium | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TableColumn.html |
| 15 | 19 | Controls | Virtualized controls are for large data | TableView ListView TreeView virtualize cells and outperform manual node lists | Use TableView or ListView for hundreds of rows | Create hundreds of HBoxes inside a VBox | ListView<Item> list = new ListView<>(items); | items.forEach(i -> vbox.getChildren().add(new ItemRow(i))); | High | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/ListView.html |
| 16 | 21 | Binding | Use property binding for derived UI state | JavaFX binding reduces imperative synchronization bugs | Bind disabled visible text and progress properties to source state | Manually update every dependent control in each event handler | saveButton.disableProperty().bind(form.validProperty().not()); | if(!valid) saveButton.setDisable(true); | High | https://openjfx.io/javadoc/21/javafx.base/javafx/beans/binding/Bindings.html |
| 17 | 23 | Binding | Use listeners sparingly | Bindings express simple relationships more clearly than listeners | Use listeners for side effects and bindings for values | Create listener chains for simple computed text | totalLabel.textProperty().bind(Bindings.format("Total: %d", total)); | count.addListener((o, a, b) -> totalLabel.setText("Total: " + b)); | Low | https://openjfx.io/javadoc/21/javafx.base/javafx/beans/value/ObservableValue.html |
| 18 | 24 | Events | Use action handlers for commands | Buttons and menu items should route to named command methods | Use setOnAction or @FXML handler methods with clear names | Put large lambdas inline for complex operations | @FXML private void handleSave(ActionEvent event) { saveCustomer(); } | saveButton.setOnAction(e -> { validate(); transform(); query(); save(); refresh(); }); | Medium | https://openjfx.io/javadoc/21/javafx.base/javafx/event/ActionEvent.html |
| 19 | 25 | Events | Use event filters for global shortcuts | Filters can intercept keyboard events before child controls consume them | Register accelerators or filters at Scene level | Add duplicate key handlers to every control | scene.getAccelerators().put(new KeyCodeCombination(KeyCode.S, SHORTCUT_DOWN), this::save); | nameField.setOnKeyPressed(...); table.setOnKeyPressed(...); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Scene.html |
| 20 | 27 | Accessibility | Expose accessible text for icon buttons | Icon-only controls need names for screen readers and tooltips | Set accessibleText and Tooltip on icon buttons | Use unlabeled graphic-only buttons | button.setAccessibleText("Refresh"); button.setTooltip(new Tooltip("Refresh")); | new Button("", refreshIcon) | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/AccessibleRole.html |
| 21 | 28 | Accessibility | Keep keyboard focus visible | Desktop users rely on focus traversal and visible focus indicators | Preserve focus rings and tab order | Remove outlines without alternative focus state | .button:focused { -fx-border-color: -brand-focus; -fx-border-width: 2; } | .button:focused { -fx-background-insets: 0; } | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Node.html |
| 22 | 29 | Accessibility | Use mnemonics for menu and form workflows | Mnemonics make desktop workflows faster and more accessible | Enable mnemonicParsing and choose unique mnemonic letters | Ignore keyboard alternatives for frequent actions | saveButton.setMnemonicParsing(true); saveButton.setText("_Save"); | saveButton.setText("Save"); | Low | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Labeled.html |
| 23 | 30 | Validation | Show validation near the field | Users should not hunt for form errors in desktop dialogs | Bind error labels or pseudo classes next to invalid controls | Show only a generic alert after submit | field.pseudoClassStateChanged(PseudoClass.getPseudoClass("invalid"), !valid); | new Alert(ERROR, "Invalid input").show(); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/css/PseudoClass.html |
| 24 | 31 | Validation | Use TextFormatter for constrained input | TextFormatter prevents invalid edits before they enter the model | Attach TextFormatter for numeric dates and masks | Parse and reject invalid text only after submit | amountField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), 0, c -> c.getControlNewText().matches("\\d*") ? c : null)); | Integer.parseInt(amountField.getText()); | Medium | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TextFormatter.html |
| 25 | 32 | Dialogs | Use modal ownership for dialogs | Dialogs should block only the relevant window and return structured results | Set owner modality and use showAndWait | Open unmanaged windows for confirmations | dialog.initOwner(stage); dialog.initModality(Modality.WINDOW_MODAL); Optional<ButtonType> result = dialog.showAndWait(); | new Stage().show(); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/stage/Modality.html |
| 26 | 33 | Dialogs | Prefer custom DialogPane over ad hoc stages | Dialog gives consistent buttons focus and result handling | Use Dialog<T> for forms confirmations and wizards | Build every modal as a new Stage manually | Dialog<Customer> dialog = new Dialog<>(); dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL); | Stage modal = new Stage(); modal.setScene(new Scene(new VBox())); | Low | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/Dialog.html |
| 27 | 34 | Images | Load images as resources | Packaged apps need resources resolved from the classpath or module path | Use getResourceAsStream for bundled assets | Use absolute local file paths in production UI | new Image(getClass().getResourceAsStream("/images/logo.png")); | new Image("file:/Users/me/Desktop/logo.png") | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/image/Image.html |
| 28 | 35 | Images | Use background loading for large images | Large image decoding can pause UI startup | Use Image(url true) or a background Task for heavy assets | Load many full-size images synchronously during startup | Image preview = new Image(url, true); | gallery.add(new Image(url)); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/image/Image.html |
| 29 | 36 | Animation | Keep animations purposeful and short | Desktop UI animations should clarify state changes without delaying work | Use 150-250ms transitions for reveal hover and selection | Animate every layout change with long timelines | FadeTransition ft = new FadeTransition(Duration.millis(180), pane); ft.setToValue(1); | new Timeline(new KeyFrame(Duration.seconds(2), ...)).play(); | Low | https://openjfx.io/javadoc/21/javafx.graphics/javafx/animation/package-summary.html |
| 30 | 37 | Animation | Respect reduced-motion contexts where possible | Some users experience motion sensitivity in desktop apps | Provide a setting to disable decorative animations | Make animation required for comprehension | if (settings.reducedMotion()) pane.setOpacity(1); else fade.play(); | alwaysSpin.play(); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/animation/Animation.html |
| 31 | 38 | Performance | Avoid recreating scenes for small state changes | Replacing whole scenes loses state and can flicker | Swap center content or update view models | Rebuild the entire Stage for every navigation click | shell.setCenter(customerView); | stage.setScene(new Scene(loadMainAgain())); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/Scene.html |
| 32 | 39 | Performance | Reuse loaded views when appropriate | FXML loading and CSS application are not free | Cache stable views or controllers for frequent navigation | Reload heavyweight screens repeatedly without need | Node settings = viewCache.computeIfAbsent("settings", this::loadSettings); | button.setOnAction(e -> shell.setCenter(loadFxml("settings.fxml"))); | Low | https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html |
| 33 | 40 | Performance | Batch observable list changes | Many single-item updates can cause repeated layout and sort work | Use setAll or addAll for bulk replacement | Loop add items one by one to visible lists | items.setAll(repository.findAll()); | for(Item item : loaded) items.add(item); | Medium | https://openjfx.io/javadoc/21/javafx.base/javafx/collections/ObservableList.html |
| 34 | 41 | Architecture | Use view models for complex screens | View models keep controller state testable and separate from controls | Expose JavaFX properties from a screen model | Store all state only inside controls | customerNameField.textProperty().bindBidirectional(viewModel.nameProperty()); | String name = customerNameField.getText(); // everywhere | Medium | https://openjfx.io/javadoc/21/javafx.base/javafx/beans/property/package-summary.html |
| 35 | 43 | Modules | Declare required JavaFX modules | Modular JavaFX apps must require the modules they use | Add javafx.controls javafx.fxml and opens controller packages | Depend on classpath accidents only | module app { requires javafx.controls; requires javafx.fxml; opens app.ui to javafx.fxml; } | module app { requires javafx.controls; } | High | https://openjfx.io/openjfx-docs/#modular |
| 36 | 44 | Packaging | Use jlink or jpackage for desktop delivery | JavaFX apps should ship with the runtime they need | Package a runtime image or native installer | Ask end users to install matching Java and JavaFX manually | jpackage --name MyApp --module app/app.Main --runtime-image build/image | java -jar app.jar | Medium | https://openjfx.io/openjfx-docs/#modular |
| 37 | 45 | Testing | Use TestFX for interaction tests | UI flows need automated coverage beyond controller unit tests | Write TestFX tests for key forms dialogs and navigation | Only manually click through releases | clickOn("#nameField").write("Alice"); clickOn("Save"); verifyThat("Saved", isVisible()); | // manual QA only | Medium | https://github.com/TestFX/TestFX |
| 38 | 47 | Theme | Prefer Primer for enterprise applications | PrimerLight and PrimerDark are neutral enough for dense business workflows | Use PrimerLight as default and PrimerDark for dark mode | Use Dracula or Cupertino as the default enterprise theme | Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet()); | Application.setUserAgentStylesheet(new Dracula().getUserAgentStylesheet()); | Medium | https://mkpaz.github.io/atlantafx/themes/ |
| 39 | 48 | Theme | Layer brand CSS after AtlantaFX | Application CSS should customize brand tokens and business states after the base theme | Add app.css to the Scene after setting AtlantaFX | Edit AtlantaFX source CSS directly | scene.getStylesheets().add(getClass().getResource("/css/app.css").toExternalForm()); | modify atlantafx-base CSS files | High | https://mkpaz.github.io/atlantafx/theming/ |
| 40 | 49 | Theme | Use looked-up colors as enterprise tokens | JavaFX looked-up colors keep brand and semantic colors reusable across controls | Define app-primary app-success app-warning app-danger on root | Repeat hex values in every selector | .root { -app-primary: #2563eb; -app-danger: #dc2626; } | .save { -fx-background-color: #2563eb; } .link { -fx-text-fill: #2563eb; } | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/doc-files/cssref.html |
| 41 | 50 | Theme | Keep theme switching centralized | Dark mode switching should not be scattered across controllers | Use a ThemeService that sets user-agent stylesheet and app CSS variants | Let each controller decide its own theme | themeService.apply(ThemeMode.DARK); | if(dark) button.setStyle(...); | Medium | https://mkpaz.github.io/atlantafx/ |
| 42 | 51 | Theme | Validate contrast for business status colors | Enterprise screens use status colors heavily and need readable contrast | Check text on success warning danger and selected row backgrounds | Assume brand colors are accessible | .status-danger { -fx-text-fill: -app-danger; } | red text on dark red background | High | https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html |
| 43 | 52 | Theme | Use AtlantaFX style classes before custom CSS | AtlantaFX exposes utility styles that reduce custom CSS drift | Prefer Styles constants or documented style classes | Create one-off class names for every button variant | saveButton.getStyleClass().add(Styles.ACCENT); | saveButton.getStyleClass().add("blue-button-42"); | Medium | https://mkpaz.github.io/atlantafx/ |
| 44 | 53 | Theme | Treat AtlantaFX as a base not the whole design system | AtlantaFX modernizes controls but enterprise UX still needs layout density and workflow rules | Define app shell navigation table density form and validation conventions | Assume theme choice alone solves enterprise usability | root.getStyleClass().add("enterprise-shell"); | only set PrimerLight and stop | High | https://mkpaz.github.io/atlantafx/ |
| 45 | 55 | Components | Use AtlantaFX controls for common app affordances | AtlantaFX provides useful controls such as Card Message ModalPane Popover and ToggleSwitch | Use built-in AtlantaFX controls before adding another dependency | Add ControlsFX for components AtlantaFX already covers | Message message = new Message("Saved", "Customer updated successfully"); | new Label("Saved") with ad hoc styling | Medium | https://mkpaz.github.io/atlantafx/ |
| 46 | 56 | Components | Add ControlsFX only for missing enterprise controls | ControlsFX is useful for specialized controls but should stay optional | Use ControlsFX for SpreadsheetView PropertySheet CheckComboBox or StatusBar needs | Add ControlsFX by default before requirements are clear | PropertySheet sheet = new PropertySheet(items); | implementation "org.controlsfx:controlsfx" with no usage | Low | https://controlsfx.github.io/ |
| 47 | 57 | Testing | Test theme-critical flows with TestFX | Theme and CSS changes can break focus visibility dialogs and button affordance | Use TestFX for login save validation and modal workflows | Only inspect AtlantaFX screens manually | clickOn("#saveButton"); verifyThat(".message", isVisible()); | manual theme QA only | Medium | https://github.com/TestFX/TestFX |
| 48 | 58 | Architecture | Use application shell plus feature workspaces | Enterprise JavaFX apps need stable navigation around changing work areas | Use BorderPane shell with navigation toolbar and central workspace | Replace the whole Stage for every feature | shell.setLeft(navigation); shell.setTop(toolbar); shell.setCenter(workspace); | stage.setScene(new Scene(loadFeature())); | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/scene/layout/BorderPane.html |
| 49 | 59 | Architecture | Use MVVM for complex enterprise screens | Large forms and tables need testable state outside the controller | Expose JavaFX properties from view models and bind controls to them | Put all screen state and validation in the controller | amountField.textProperty().bindBidirectional(vm.amountProperty()); | controller.amount = amountField.getText(); | High | https://openjfx.io/javadoc/21/javafx.base/javafx/beans/property/package-summary.html |
| 50 | 60 | Architecture | Inject services into controllers | Enterprise controllers should coordinate UI and call application services | Use a controller factory or DI container for services | Create database connections inside FXML controllers | loader.setControllerFactory(type -> injector.getInstance(type)); | new CustomerRepository(new DriverManager(...)) | High | https://openjfx.io/javadoc/21/javafx.fxml/javafx/fxml/FXMLLoader.html |
| 51 | 61 | Navigation | Use role-aware navigation models | Menus toolbars and shortcuts should reflect the same permission model | Build navigation items from commands with required roles | Hide buttons in one place and leave shortcuts enabled | command.enabledProperty().bind(permissionService.allowed("invoice.approve")); | approveButton.setVisible(false); | High | |
| 52 | 62 | Workflow | Represent workflow states visibly | Approval and processing screens need clear business state signals | Use semantic badges row styles and disabled actions by workflow state | Use only free text status columns | row.pseudoClassStateChanged(PseudoClass.getPseudoClass("blocked"), item.isBlocked()); | statusCol.setText("B"); | Medium | https://openjfx.io/javadoc/21/javafx.graphics/javafx/css/PseudoClass.html |
| 53 | 63 | TableView | Design TableView for high-density enterprise data | Enterprise users scan compare sort filter and act on rows for long periods | Use compact row height clear columns sorting filtering and selection summary | Use card grids for large tabular datasets | table.getStyleClass().add("dense-table"); table.getSortOrder().setAll(updatedAtCol); | new TilePane(customerCards) | High | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TableView.html |
| 54 | 64 | TableView | Keep row actions predictable | Inline actions in dense tables should be limited and permission-aware | Use context menus or a side detail panel for secondary actions | Place many buttons in every row | table.setRowFactory(tv -> { TableRow<Order> row = new TableRow<>(); row.setContextMenu(orderMenu); return row; }); | row contains Edit Delete Approve Print Email buttons | Medium | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/ContextMenu.html |
| 55 | 65 | TableView | Use server-side paging for large enterprise datasets | Desktop clients should not load entire enterprise tables into memory | Fetch pages or filtered slices from services | Load all records and filter in the UI | Page<Customer> page = customerService.search(criteria, pageRequest); | customerRepository.findAll() | High | |
| 56 | 66 | Forms | Use form sections for enterprise data entry | Long enterprise forms need grouping and progressive disclosure | Group fields into titled sections with validation summaries | Place dozens of inputs in one unbroken GridPane | TitledPane billing = new TitledPane("Billing", billingForm); | new GridPane with 80 controls | Medium | https://openjfx.io/javadoc/21/javafx.controls/javafx/scene/control/TitledPane.html |
| 57 | 67 | Forms | Provide validation summary plus field errors | Enterprise forms often need multiple corrections before submission | Show a summary at top and field-level messages near controls | Show only one modal alert after Save | summary.setItems(vm.validationErrors()); field.pseudoClassStateChanged(INVALID, fieldError); | new Alert(ERROR, "Invalid form").showAndWait(); | High | |
| 58 | 68 | Tasks | Make long operations cancellable | Enterprise imports exports sync and reports need cancel paths | Expose cancel button bound to Task running state | Force users to wait or kill the app | cancelButton.setOnAction(e -> task.cancel()); | runReportButton.setDisable(true); | High | https://openjfx.io/javadoc/21/javafx.graphics/javafx/concurrent/Task.html |
| 59 | 69 | Tasks | Surface retryable errors without losing context | Network and service failures should preserve user input and next action | Show inline retry messages and keep form/table state | Clear the screen on service failure | message.setDescription("Could not save. Check connection and retry."); | loadErrorScene(); | High | |
| 60 | 70 | Audit | Log business actions through services | Enterprise desktop apps need traceability for sensitive changes | Record user action entity result and timestamp in service layer | Log only UI button clicks | audit.log(user, "invoice.approve", invoiceId, SUCCESS); | System.out.println("clicked approve"); | Medium | |
| 61 | 71 | Configuration | Separate user preferences from application config | Enterprise apps need deploy-time config and per-user preferences | Use config files for endpoints and Preferences for UI choices | Hard-code environment URLs and window state | Preferences.userNodeForPackage(App.class).put("theme", "dark"); | private static final String API = "http://localhost:8080"; | Medium | https://docs.oracle.com/en/java/javase/21/docs/api/java.prefs/java/util/prefs/Preferences.html |
| 62 | 72 | Deployment | Package resources and themes inside the runtime image | AtlantaFX app CSS icons and FXML must be available after jpackage | Load resources from classpath or module resources | Load theme files from developer machine paths | getClass().getResource("/css/app.css").toExternalForm(); | new File("src/main/resources/css/app.css").toURI() | High | https://openjfx.io/openjfx-docs/#modular |
| 63 | 74 | Testing | Cover enterprise happy path and failure path | Enterprise UI tests should verify save validation permission and service failure flows | Use TestFX for core workflows and service fakes | Only test controller methods without UI interaction | clickOn("Save"); verifyThat("Customer saved", isVisible()); | controller.save(); assertTrue(saved); | High | https://github.com/TestFX/TestFX |
| 64 | 75 | Dependencies | Keep optional UI libraries behind actual needs | AtlantaFX should be default but additional libraries should be justified | Start with JavaFX AtlantaFX Ikonli TestFX and add ControlsFX only for missing controls | Adopt many UI libraries at project start | dependencies { implementation("io.github.mkpaz:atlantafx-base:2.1.0") } | implementation controlsfx gemsfx tilesfx materialfx all at once | Medium |