Skip to content

Shiny Developer Interview Questions & Answers

40 questions for Shiny Developer interviews — 2 easy, 29 medium, 9 hard.

About the Shiny Developer role

A Shiny Developer builds interactive web dashboards and tools in R that let business users explore data, run what-if scenarios and download reports. The role needs strong reactive programming, UI design with bslib, performance tuning, testing and deployment on Posit Connect or Docker. Analytics firms and pharma companies hire Shiny developers to turn models and analyses into self-service applications. Basic HTML and CSS knowledge helps when customising layouts.

Shiny reactivity bslib UI Performance tuning Posit Connect Docker
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

An R script must run every morning at 7 AM and email a report. How do you schedule it?

Save to ▾
Show answer
On a Linux server I schedule Rscript with cron, for example 0 7 * * * Rscript /path/report.R, and on Windows with Task Scheduler or the taskscheduleR package. Posit Connect can schedule Quarto reports and email them directly. GitHub Actions can run scheduled workflows for non-sensitive jobs. The script itself renders the report and sends it with blastula or emayili. I add logging and an alert on failure so silent misses are caught.
02

What is the difference between geom_bar() and geom_col()?

Save to ▾
Show answer
geom_bar() counts rows for each x value by default using stat = 'count', so you give only x, for example the number of orders per city from raw data. geom_col() uses y values as they are, so you supply already summarised data, for example total sales per city. geom_bar(stat = 'identity') is equivalent to geom_col(). Using geom_bar() with a y aesthetic gives an error, which is a common beginner issue.
03

How do you debug an R function?

Save to ▾
Show answer
After an error, traceback() shows the call stack so I can see which function failed. I insert browser() inside a function to pause execution and inspect variables interactively, stepping with n and continuing with c. debug(fn) or debugonce(fn) enters the browser on the next call without editing code. options(error = recover) lets me choose a frame to inspect after an error. RStudio breakpoints offer the same features visually.
04

What does the ... (dots) argument do in an R function?

Save to ▾
Show answer
The dots let a function accept any number of additional arguments and pass them on to another function. For example, my_plot <- function(df, ...) ggplot(df, aes(x, y)) + geom_point(...) lets callers pass colour or size without declaring them. You can capture dots with list(...) or count them with ...length(). The downside is that misspelled argument names are silently ignored, so document them and validate with rlang::check_dots_used() in packages.
05

What is Rcpp and when would you use it?

Save to ▾
Show answer
Rcpp is a package that lets you write C++ functions and call them from R with almost no boilerplate, using cppFunction() for small snippets or sourceCpp() for files. It is used when R code has loops that cannot be vectorized, such as recursive calculations, simulations with dependence between steps or custom algorithms. Many popular packages, including dplyr and data.table internals, rely on compiled code. Use it only after profiling shows a real bottleneck.
06

How do you handle errors in R code?

Save to ▾
Show answer
tryCatch() catches errors and warnings so a script can recover or log them; finally runs cleanup. read_safe <- function(path) { tryCatch({ readr::read_csv(path, show_col_types = FALSE) }, warning = function(w) { message('Warning in ', path, ': ', conditionMessage(w)) NULL }, error = function(e) { message('Failed: ', path, ' - ', conditionMessage(e)) NULL }, finally = message('Done: ', path)) }
07

Your team wants to share a Shiny app with 200 internal users. What deployment options exist?

Save to ▾
Show answer
Options include shinyapps.io, a hosted service that is quickest to start but may not suit sensitive internal data, Posit Connect, a commercial on-premise or cloud server with authentication, scheduling and scaling, the free open-source Shiny Server on a Linux machine, and a Docker container behind a reverse proxy like Nginx, often with ShinyProxy for authentication. For internal data, I would choose Connect or containers inside the company network with single sign-on.
08

What are facets in ggplot2?

Save to ▾
Show answer
Facets split a plot into small multiples, one panel per category, sharing the same axes and geoms. facet_wrap(~ region) wraps panels for one variable, and facet_grid(rows = vars(segment), cols = vars(year)) creates a grid for two variables. scales = 'free_y' lets each panel have its own y-axis. Facets are often clearer than one chart with many overlapping coloured lines, for example comparing sales trends across ten states.
09

You are analysing a national survey where some groups were deliberately oversampled. How do you compute correct averages?

Save to ▾
Show answer
Simple averages would be biased toward oversampled groups, so I use the survey weights provided with the data. In R, the survey package defines the design with svydesign(ids = ~psu, strata = ~stratum, weights = ~wt, data = df), then svymean(), svytotal() and svyglm() give weighted estimates with correct standard errors that account for clustering and stratification. The srvyr package offers the same with dplyr-style syntax.
10

Tell me about feedback you received in a code review and what you changed.

Save to ▾
Show answer
Situation: in my first quarter, a senior reviewed my R reporting script. Task: address the review comments. Action: the main feedback was that my 300-line script mixed data loading, cleaning and plotting with repeated code. I split it into small functions, added input checks and moved constants to a config file. Result: the next report took half the time to build, and I now structure every new script this way from the start.
11

How do you validate inputs inside an R function?

Save to ▾
Show answer
Fail early with clear messages using stopifnot(), stop() and match.arg(). summarise_kpi <- function(df, metric = c('revenue', 'orders', 'aov'), min_date = NULL) { stopifnot(is.data.frame(df), nrow(df) > 0) metric <- match.arg(metric) if (!is.null(min_date) && !inherits(min_date, 'Date')) { stop('min_date must be a Date', call. = FALSE) } required <- c('date', metric) missing <- setdiff(required, names(df)) if (length(missing)) stop('Missing columns: ', toString(missing)) # ... compute }
12

Explain reactivity in Shiny.

Save to ▾
Show answer
Shiny uses reactive programming: outputs automatically update when the inputs they depend on change. Inputs are reactive sources, outputs such as renderPlot() are reactive endpoints, and reactive() expressions are cached intermediate results that recompute only when their dependencies change. For example, filtering data once in a reactive() and using it in three charts avoids filtering three times. Shiny tracks dependencies automatically, so you do not write explicit event handlers for most cases.
13

Your scatter plot of 200,000 points looks like a solid blob. How do you fix it?

Save to ▾
Show answer
This is overplotting. I reduce point size and add transparency with geom_point(alpha = 0.1, size = 0.5). Better options are geom_hex() or geom_bin2d(), which show density as colour, or geom_density_2d() contours. I could also plot a random sample for shape and a smoothed trend with geom_smooth() on the full data. If x values are discrete, geom_jitter() spreads points. The goal is to make density visible, not every point.
14

How do you run R code in parallel?

Save to ▾
Show answer
The parallel package ships with R: mclapply() forks processes on Linux and macOS, while parLapply() uses a socket cluster that also works on Windows. The future framework gives a cleaner interface where you choose a plan, such as plan(multisession), and then use future_map() from furrr or future_lapply(). Parallelism helps when each task is independent and heavy, such as model fitting per group, but adds overhead for small tasks and multiplies memory use.
15

How do you add labels and annotations to a ggplot chart?

Save to ▾
Show answer
Use geom_text() or geom_label() for data labels and annotate() for one-off notes. ggplot(city_sales, aes(reorder(city, sales), sales)) + geom_col(fill = 'steelblue') + geom_text(aes(label = scales::comma(sales)), hjust = -0.1, size = 3.5) + annotate('text', x = 2, y = 9e5, label = 'Festive season push') + coord_flip() + expand_limits(y = 1.1e6) + labs(x = NULL, y = 'Sales (INR)')
16

What do geom_smooth() and stat_summary() do?

Save to ▾
Show answer
geom_smooth() adds a fitted trend line with a confidence band, using loess for small data and a GAM for large data by default, or method = 'lm' for a straight line. stat_summary() computes summaries of y for each x, for example stat_summary(fun = mean, geom = 'point') to plot group means, or fun.data = mean_cl_normal for error bars. Both let you show patterns without precomputing summaries.
17

The same 40 lines of cleaning code are copied into ten scripts. What would you do?

Save to ▾
Show answer
I would extract the logic into well-named functions with arguments for the parts that differ, then move them into one shared file loaded with source() or, better, a small internal R package. A package gives documentation with roxygen2, tests with testthat and versioning, so every script uses the same tested logic. Fixing a bug then happens in one place. I would migrate the scripts gradually and compare outputs to confirm nothing changed.
18

What is the difference between coord_cartesian() limits and scale limits in ggplot2?

Save to ▾
Show answer
Setting limits with scale_y_continuous(limits = c(0, 100)) or ylim() removes data outside the range before statistics are calculated, which changes boxplots, smooth lines and bar totals and gives warnings about removed rows. coord_cartesian(ylim = c(0, 100)) only zooms the view, keeping all data in calculations. So for zooming into part of a chart without altering the summary, coord_cartesian() is the correct choice.
19

How do you write unit tests for an R function?

Save to ▾
Show answer
testthat organises tests into files under tests/testthat/ using expectations. # tests/testthat/test-gst.R test_that('gst_price adds 18 percent by default', { expect_equal(gst_price(100), 118) expect_equal(gst_price(c(100, 200)), c(118, 236)) }) test_that('gst_price rejects text input', { expect_error(gst_price('abc'), 'numeric') }) # run all tests devtools::test()
20

What is plumber in R?

Save to ▾
Show answer
plumber turns R functions into REST API endpoints using special comments. You annotate a function with #* @get /predict and its arguments become query parameters, then run the API with pr_run(). Other systems, such as a Java web app or a Python service, can call the R model over HTTP and receive JSON. It is commonly used to serve model predictions, and it can be deployed in Docker or on Posit Connect.
21

How do you create maps in R?

Save to ▾
Show answer
The sf package stores spatial data as data frames with a geometry column, and ggplot2's geom_sf() draws them. You read shapefiles or GeoJSON with st_read(), join your data, for example sales by state, and fill polygons by value to make a choropleth. tmap is a dedicated thematic mapping package and leaflet builds interactive web maps. Coordinate reference systems must match, and st_transform() reprojects layers when they do not.
22

What is the purrr map family?

Save to ▾
Show answer
purrr's map() applies a function to each element of a list or vector and always returns a list. Typed variants return specific types: map_dbl(), map_chr(), map_lgl() and map_int(), and they error if a result has the wrong type, which is safer than sapply(). map2() iterates over two inputs, pmap() over many, and walk() is used for side effects like saving files. Formula shortcuts such as ~ .x * 2 keep code short.
23

What is the difference between Quarto and R Markdown?

Save to ▾
Show answer
Quarto is the next-generation publishing system from Posit. It works with R, Python, Julia and Observable, and is a standalone command-line tool rather than an R package. It uses the same knitr engine for R and similar syntax, with chunk options written as #| comments. Quarto adds consistent support for websites, books, presentations and dashboards. R Markdown is still supported, but new projects increasingly use Quarto, especially in mixed R and Python teams.
24

What is a pure function and why prefer them in data pipelines?

Save to ▾
Show answer
A pure function always returns the same output for the same inputs and has no side effects, such as modifying global variables, writing files or printing. Pure functions are easy to test, reuse, cache and run in parallel, because they do not depend on hidden state. In data pipelines, I keep transformations pure, taking a data frame and returning a new one, and isolate side effects such as reading and writing in a few clearly named functions.
25

Describe a disagreement with a teammate about a technical approach.

Save to ▾
Show answer
Situation: a teammate wanted to build our reporting pipeline in Excel macros, while I preferred R scripts. Task: agree on an approach quickly. Action: instead of arguing, we listed requirements together: refresh frequency, data size and who would maintain it. I built a small R prototype in a day to compare. Result: we chose R for the pipeline and kept an Excel output for users, and my teammate later helped improve the formatting.
26

Some stakeholders are colour-blind. How do you design charts for them?

Save to ▾
Show answer
I use colour-blind-safe palettes such as viridis with scale_colour_viridis_d() or the Okabe-Ito palette, and avoid red-green contrasts. I never rely on colour alone: I add direct labels, different shapes or line types, and facets. I limit categories to around six colours and highlight one key series while greying the rest. I also check charts with a colour-blindness simulator, such as the colorblindr package, before sharing.
27

The finance team wants the daily output as a formatted Excel file, not a CSV. How do you produce it?

Save to ▾
Show answer
I use openxlsx2 or openxlsx to write a workbook with multiple sheets, formatted headers, number formats for INR, column widths, frozen panes and conditional formatting, all from R. writexl is a lightweight option for plain sheets without formatting. I create a template once with the team's preferred layout and generate the file in the scheduled script, then email or upload it to a shared drive. This removes manual formatting every day.
28

How do you use Reduce(), Filter() and Map() in base R?

Save to ▾
Show answer
These higher-order functions come from functional programming. nums <- 1:10 Filter(function(x) x %% 2 == 0, nums) # even numbers Map(function(a, b) a + b, 1:3, 4:6) # list: 5 7 9 Reduce(`+`, 1:5, accumulate = TRUE) # 1 3 6 10 15 # join many data frames on a common key dfs <- list(sales, targets, regions) Reduce(function(x, y) merge(x, y, by = 'region_id'), dfs) # purrr equivalents: keep(), map2(), reduce()
29

Tell me about a chart or visualization that changed a business decision.

Save to ▾
Show answer
Situation: the operations team believed late deliveries were spread evenly across the week. Task: I was asked to review delivery data. Action: in R I built a heatmap of late deliveries by weekday and hour with ggplot2, which showed a sharp cluster on Monday mornings linked to weekend order backlog. Result: management added a Sunday evening shift, and late deliveries dropped by about 18 percent the next quarter.
30

How do you add logging to a production R script?

Save to ▾
Show answer
The logger package gives levelled, timestamped logs written to the console or a file. library(logger) log_appender(appender_file('logs/etl.log')) log_threshold(INFO) log_info('ETL started') res <- tryCatch({ df <- load_orders() log_info('Loaded {nrow(df)} rows') df }, error = function(e) { log_error('Load failed: {conditionMessage(e)}') quit(status = 1) }) log_info('ETL finished')
31

Code reviews show inconsistent R code style across the team. How would you standardise it?

Save to ▾
Show answer
I would adopt the tidyverse style guide as the team standard, then automate it rather than relying on reviewers. styler reformats code automatically, including an RStudio add-in, and lintr flags issues such as long lines, unused variables and inconsistent naming. I add lintr to a pre-commit hook or a GitHub Actions check so pull requests are checked automatically. A short template for new scripts and projects helps new joiners follow the same structure.
32

How does a random forest work and which R packages implement it?

Save to ▾
Show answer
A random forest builds many decision trees, each on a bootstrap sample of rows and considering a random subset of features at each split, then averages their predictions for regression or takes a majority vote for classification. This reduces the variance of single trees and handles non-linear relationships well. In R, randomForest is the classic package and ranger is a much faster implementation. Key parameters are the number of trees, mtry and minimum node size.
33

What is the structure of an R package?

Save to ▾
Show answer
A minimal package has a DESCRIPTION file with metadata and dependencies, a NAMESPACE file listing exported functions, an R/ folder with function code, a man/ folder for documentation and usually tests/ for testthat tests. The usethis and devtools packages create and manage this: usethis::create_package(), use_r(), use_test(), and devtools::document(), test() and check(). Documentation is written as roxygen2 comments above each function. Internal packages are ideal for sharing team utilities.
34

Tell me about a dashboard or app you built in R.

Save to ▾
Show answer
Situation: regional sales managers waited for a weekly emailed PDF and could not drill into their own data. Task: give them self-service access. Action: I built a Shiny app with filters by region, product and period, used pre-aggregated tables for speed and deployed it on the internal server with login. I ran a short demo and gathered feedback. Result: over 40 managers used it weekly, and ad hoc data requests to our team fell by about half.
35

What is the difference between reactive(), observe() and observeEvent() in Shiny?

Save to ▾
Show answer
reactive() creates a cached value that other code can read, such as filtered data, and it is lazy, recomputing only when needed. observe() runs code for its side effects whenever any reactive dependency changes, returning nothing. observeEvent() runs side effects only when a specific event fires, such as a button click, ignoring other dependencies. Use reactive() for computing values and observeEvent() for actions like saving data or showing notifications.
36

How do you generate the same report for each of 20 regions automatically?

Save to ▾
Show answer
Use a parameterized Quarto or R Markdown report and render it in a loop. # report.qmd header: # params: # region: 'North' # then use params$region in the code regions <- unique(sales$region) purrr::walk(regions, function(r) { quarto::quarto_render( 'report.qmd', execute_params = list(region = r), output_file = paste0('report_', r, '.html') ) })
37

How would you create a reusable branded theme for all team charts?

Save to ▾
Show answer
Wrap theme settings and palettes in functions stored in an internal package: theme_team <- function(base_size = 12) { theme_minimal(base_size = base_size) + theme( plot.title = element_text(face = 'bold', colour = '#1F3A5F'), panel.grid.minor = element_blank(), legend.position = 'top' ) } scale_fill_team <- function(...) { scale_fill_manual(values = c('#1F3A5F', '#F28C28', '#2A9D8F'), ...) } ggplot(df, aes(x, y, fill = g)) + geom_col() + theme_team() + scale_fill_team()
38

A customer table has multiple records per customer. How do you keep only the latest record for each?

Save to ▾
Show answer
I group by customer_id and keep the row with the latest updated_at using slice_max(updated_at, n = 1, with_ties = FALSE), then ungroup. An alternative is arrange(desc(updated_at)) followed by distinct(customer_id, .keep_all = TRUE). I check that updated_at is a proper date-time and has no NAs, since missing timestamps would be dropped or mis-sorted. Finally I confirm that n_distinct(customer_id) equals the number of rows.
39

What is renv and why should you use it?

Save to ▾
Show answer
renv is a package that creates a project-specific library and records exact package versions in a renv.lock file. renv::init() sets up the project, renv::snapshot() updates the lockfile and renv::restore() installs the same versions on another machine or server. This prevents code from breaking when packages update, makes analysis reproducible months later and is essential before deploying scripts or Shiny apps to production.
40

A simulation loop runs for 40 minutes. How would you speed it up?

Save to ▾
Show answer
First I profile with profvis to find the slow lines. Then I try vectorizing, for example generating all random numbers at once with rnorm(n * reps) and using matrix operations instead of nested loops. I preallocate outputs instead of growing them. If iterations are independent, I run them in parallel with future and furrr. If a tight loop cannot be vectorized, I rewrite it in C++ with Rcpp, which often gives a 10 to 100 times speed-up.

Frequently asked questions

What does a Shiny Developer do?

A Shiny Developer builds interactive web dashboards and tools in R that let business users explore data, run what-if scenarios and download reports. The role needs strong reactive programming, UI design with bslib, performance tuning, testing and deployment on Posit Connect or Docker. Analytics firms and pharma companies hire Shiny developers to turn models and analyses into self-service applications. Basic HTML and CSS knowledge helps when customising layouts.

Which skills are tested in a Shiny Developer interview?

Expect questions on Shiny reactivity, bslib UI, Performance tuning, Posit Connect, Docker. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Shiny Developer interview?

Revise Reports, Shiny and Production R, Data Visualization with ggplot2, Functions and Functional Programming first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.